From 2b454a178662437ac8812277397581eb3797908e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 29 Oct 2015 12:41:13 +0100 Subject: [PATCH 001/240] Fix rake import:logicgraph. --- lib/tasks/import.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 473d41cb7..6c5e37fb4 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -41,7 +41,7 @@ namespace :import do desc 'Import logic graph.' task :logicgraph => :environment do - RakeHelper.import_logicgraph(ENV['EMAIL']) + RakeHelper::LogicGraph.import(ENV['EMAIL']) end desc 'Import keywords starting with P.' From 353f5670601cd45216a420745dded50a73ac5616 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 13:47:21 +0200 Subject: [PATCH 002/240] Remove unused method. --- app/models/proof_attempt.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models/proof_attempt.rb b/app/models/proof_attempt.rb index f7f0d698c..a55f3ed20 100644 --- a/app/models/proof_attempt.rb +++ b/app/models/proof_attempt.rb @@ -43,10 +43,6 @@ def self.find_with_locid(locid, _iri = nil) where(locid: locid).first end - def used_sentences - @used_sentences ||= used_axioms + used_theorems - end - def set_default_proof_status self.proof_status ||= ProofStatus.find(ProofStatus::DEFAULT_OPEN_STATUS) end From ae2c2796b22ac1f7070e2c4c3617989ba56fa276 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 14:05:40 +0200 Subject: [PATCH 003/240] Fix TacticScript factory. --- app/models/tactic_script.rb | 2 +- spec/factories/tactic_script_factory.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/tactic_script.rb b/app/models/tactic_script.rb index a5f040555..a741d4393 100644 --- a/app/models/tactic_script.rb +++ b/app/models/tactic_script.rb @@ -1,7 +1,7 @@ class TacticScript < ActiveRecord::Base belongs_to :proof_attempt has_many :extra_options, class_name: TacticScriptExtraOption.to_s - attr_accessible :time_limit + attr_accessible :time_limit def to_s {time_limit: time_limit, diff --git a/spec/factories/tactic_script_factory.rb b/spec/factories/tactic_script_factory.rb index 9c67e63e2..366970e02 100644 --- a/spec/factories/tactic_script_factory.rb +++ b/spec/factories/tactic_script_factory.rb @@ -5,7 +5,7 @@ trait :with_extra_options do after(:build) do |tactic_script| - extra_options = [1,2].map { build :tactic_script_extra_options } + extra_options = [1,2].map { build :tactic_script_extra_option } tactic_script.extra_options = extra_options end From 56e6265eef4a0975bd42dba3009aa8aab494fba5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 14:05:53 +0200 Subject: [PATCH 004/240] Test to_s on TacticScript. --- spec/models/tactic_script_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/models/tactic_script_spec.rb b/spec/models/tactic_script_spec.rb index d5a3d1e44..ea4bec3ec 100644 --- a/spec/models/tactic_script_spec.rb +++ b/spec/models/tactic_script_spec.rb @@ -5,4 +5,19 @@ it { should belong_to(:proof_attempt) } it { should have_many(:extra_options) } end + + let(:tactic_script) { create :tactic_script, :with_extra_options } + subject { tactic_script } + + context 'to_s' do + it 'contains the time_limit' do + expect(subject.to_s).to include(subject.time_limit.to_s) + end + + it 'contains the extra options' do + subject.extra_options.each do |extra_option| + expect(subject.to_s).to include(extra_option.to_s) + end + end + end end From 83e9a33dffce5818f636c7b045311cf122a34679 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 14:43:48 +0200 Subject: [PATCH 005/240] Change delegates to has_* ActiveRecord relations. --- app/models/proof_attempt.rb | 2 +- app/models/proof_attempt_configuration.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/proof_attempt.rb b/app/models/proof_attempt.rb index a55f3ed20..8cca241c5 100644 --- a/app/models/proof_attempt.rb +++ b/app/models/proof_attempt.rb @@ -37,7 +37,7 @@ class ProofAttempt < ActiveRecord::Base scope :latest, order('number DESC') - delegate :ontology, to: :theorem + has_one :ontology, through: :theorem def self.find_with_locid(locid, _iri = nil) where(locid: locid).first diff --git a/app/models/proof_attempt_configuration.rb b/app/models/proof_attempt_configuration.rb index 7b2c33a71..3906287d3 100644 --- a/app/models/proof_attempt_configuration.rb +++ b/app/models/proof_attempt_configuration.rb @@ -8,8 +8,8 @@ class ProofAttemptConfiguration < ActiveRecord::Base validates :proof_attempt, presence: true - delegate :axioms, to: :axiom_selection - delegate :ontology, to: :proof_attempt + has_many :axioms, through: :axiom_selection + has_one :ontology, through: :proof_attempt def empty? [logic_mapping, prover, timeout, axioms].all?(&:blank?) From 6e7a0ef4c702f2ae2a8c31c8e0436949fb6808d8 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 14:44:41 +0200 Subject: [PATCH 006/240] Add 'empty?' spec. --- .../proof_attempt_configuration_spec.rb | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/spec/models/proof_attempt_configuration_spec.rb b/spec/models/proof_attempt_configuration_spec.rb index 6d9780214..e257d7ac9 100644 --- a/spec/models/proof_attempt_configuration_spec.rb +++ b/spec/models/proof_attempt_configuration_spec.rb @@ -7,4 +7,46 @@ it { should belong_to(:axiom_selection) } it { should have_one(:proof_attempt) } end + + context 'Methods' do + let(:proof_attempt) { create :proof_attempt } + let(:proof_attempt_configuration) do + proof_attempt.proof_attempt_configuration + end + subject { proof_attempt_configuration } + + context 'empty?' do + before do + subject.timeout = nil + subject.logic_mapping = nil + subject.prover = nil + subject.axiom_selection.axioms = [] + end + + it 'empty proof attempt configuration' do + expect(subject.empty?).to be(true) + end + + it 'with timeout set' do + subject.timeout = 1 + expect(subject.empty?).to be(false) + end + + it 'with logic_mapping set' do + subject.logic_mapping = LogicMapping.first + expect(subject.empty?).to be(false) + end + + it 'with prover set' do + subject.prover = Prover.first + expect(subject.empty?).to be(false) + end + + it 'with axiom_selection set' do + create :axiom + subject.axiom_selection.axioms = [Axiom.first] + expect(subject.empty?).to be(false) + end + end + end end From 80af46b05711efde86eae411ee0c11c70433527d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 15:52:44 +0200 Subject: [PATCH 007/240] Simplify sine spec by adding trait. --- spec/factories/axiom_selection_factory.rb | 30 +++++++++++++++++++++++ spec/models/sine_axiom_selection_spec.rb | 28 +++------------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/spec/factories/axiom_selection_factory.rb b/spec/factories/axiom_selection_factory.rb index 76eaff31d..ca0a98700 100644 --- a/spec/factories/axiom_selection_factory.rb +++ b/spec/factories/axiom_selection_factory.rb @@ -9,5 +9,35 @@ commonness_threshold { 0 } depth_limit { -1 } tolerance { 1.0 } + + # Allow to pass arguments that are not model attributes. + transient do + ontology_fixture_file nil + end + + trait :with_auxiliary_objects do + after(:create) do |sas, evaluator| + if evaluator.ontology_fixture_file.nil? + raise 'ontology_fixture_file must be set for :with_auxiliary_objects trait' + end + repository = create :repository + ontology_fixture_file = evaluator.ontology_fixture_file + + parent_ontology_version = + version_for_file(repository, + ontology_file(*ontology_fixture_file)) + parent_ontology_version.parse + parent_ontology = parent_ontology_version.ontology + + ontology = parent_ontology.children. + where(name: 'SubclassToleranceOnePointFive').first + theorem = ontology.theorems.first + + proof_attempt = create :proof_attempt, theorem: theorem + proof_attempt_configuration = proof_attempt.proof_attempt_configuration + proof_attempt_configuration.axiom_selection = sas.axiom_selection + sas.axiom_selection.proof_attempt_configurations = [proof_attempt_configuration] + end + end end end diff --git a/spec/models/sine_axiom_selection_spec.rb b/spec/models/sine_axiom_selection_spec.rb index 0e5faca7a..83fe3a320 100644 --- a/spec/models/sine_axiom_selection_spec.rb +++ b/spec/models/sine_axiom_selection_spec.rb @@ -99,36 +99,16 @@ context 'calling SInE' do setup_hets - let(:repository) { create :repository } - let(:ontology_fixture_file) { %w(prove/Subclass casl) } let(:ontology_filepath) { ontology_fixture_file.join('.') } - before { stub_hets_for(ontology_filepath) } - let(:parent_ontology_version) do - version = version_for_file(repository, - ontology_file(*ontology_fixture_file)) - version.parse - version - end - - let(:parent_ontology) { parent_ontology_version.ontology } - - let(:ontology) do - parent_ontology.children.where(name: 'SubclassToleranceOnePointFive').first + let(:sine_axiom_selection) do + create :sine_axiom_selection, + :with_auxiliary_objects, + ontology_fixture_file: ontology_fixture_file end - let(:theorem) { ontology.theorems.first } - - let(:proof_attempt) { create :proof_attempt, theorem: theorem } - let(:sine_axiom_selection) { create :sine_axiom_selection } subject { sine_axiom_selection } - let!(:proof_attempt_configuration) do - pac = proof_attempt.proof_attempt_configuration - pac.axiom_selection = subject.axiom_selection - subject.axiom_selection.proof_attempt_configurations = [pac] - pac - end context 'not preprocessing if already preprocessed once' do let(:proof_attempt_previous) { create :proof_attempt, theorem: theorem } From 4b93f5ed02dc682640d4ef8943a78fec9633287c Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Mon, 2 Nov 2015 10:04:06 +0100 Subject: [PATCH 008/240] Delete categories from view in search response. --- app/views/shared/_ontology_search_response.html.haml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/views/shared/_ontology_search_response.html.haml b/app/views/shared/_ontology_search_response.html.haml index 7b199bb1a..fc49e6d6d 100644 --- a/app/views/shared/_ontology_search_response.html.haml +++ b/app/views/shared/_ontology_search_response.html.haml @@ -6,6 +6,3 @@ = response.file_extension .repository= fancy_link(response.repository) .content= response.description if response.description.present? - .categories - - response.categories.each do |category| - = link_to category, category From 8c6f785edbeb75a6c8ceb84d7cb9c1151c5ac41e Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Mon, 2 Nov 2015 11:53:16 +0100 Subject: [PATCH 009/240] Add test search for an ontology with a category. --- features/OntologySearch.feature | 7 ++++++- features/step_definitions/ontology_search_steps.rb | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/features/OntologySearch.feature b/features/OntologySearch.feature index 9a2d14949..8ce0427c7 100644 --- a/features/OntologySearch.feature +++ b/features/OntologySearch.feature @@ -119,4 +119,9 @@ Scenario: I want to search for a not existing ontology in a repository Then I should see all ontologies in that repository And I should not see ontologies from other repositories When I type in a ontology name I'm searching for which is not existing - Then I should not see the ontology \ No newline at end of file + Then I should not see the ontology + +Scenario: I want to search for a ontology with a category + Given there is an ontology with a category + When I open the ontologies overview page + Then I should see all ontologies with a category \ No newline at end of file diff --git a/features/step_definitions/ontology_search_steps.rb b/features/step_definitions/ontology_search_steps.rb index a183d2180..76ccb0fc2 100644 --- a/features/step_definitions/ontology_search_steps.rb +++ b/features/step_definitions/ontology_search_steps.rb @@ -207,3 +207,13 @@ page.should_not have_content('OntologyThree') end end + +Given(/^there is an ontology with a category$/) do + @ont_with_cat = FactoryGirl.create :ontology + @cat = FactoryGirl.create :category + @ont_with_cat.categories << @cat +end + +Then(/^I should see all ontologies with a category$/) do + page.should have_content(@ont_with_cat.name) +end From dee088f5b37d0c19e4f22267db263dab94ee1155 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 15:52:57 +0200 Subject: [PATCH 010/240] Add sine methods specs. --- spec/models/sine_axiom_selection_spec.rb | 40 +++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/spec/models/sine_axiom_selection_spec.rb b/spec/models/sine_axiom_selection_spec.rb index 83fe3a320..204493b97 100644 --- a/spec/models/sine_axiom_selection_spec.rb +++ b/spec/models/sine_axiom_selection_spec.rb @@ -97,6 +97,41 @@ end end + context 'Methods' do + setup_hets + let(:ontology_fixture_file) { %w(prove/Subclass casl) } + let(:ontology_filepath) { ontology_fixture_file.join('.') } + before { stub_hets_for(ontology_filepath) } + + let(:sine_axiom_selection) do + create :sine_axiom_selection, + :with_auxiliary_objects, + ontology_fixture_file: ontology_fixture_file + end + subject { sine_axiom_selection } + + context 'cleanup' do + [SineSymbolCommonness, SineSymbolAxiomTrigger].each do |klass| + it "remove all #{klass} objects" do + subject.call + subject.send(:cleanup) + expect(klass.count).to eq(0) + end + end + end + + context 'destroy' do + before do + allow(subject).to receive(:cleanup) + end + + it 'calls cleanup' do + subject.destroy + expect(subject).to have_received(:cleanup) + end + end + end + context 'calling SInE' do setup_hets let(:ontology_fixture_file) { %w(prove/Subclass casl) } @@ -111,7 +146,10 @@ subject { sine_axiom_selection } context 'not preprocessing if already preprocessed once' do - let(:proof_attempt_previous) { create :proof_attempt, theorem: theorem } + let(:proof_attempt) do + subject.axiom_selection.proof_attempt_configurations.first.proof_attempt + end + let(:proof_attempt_previous) { create :proof_attempt, theorem: subject.goal } let(:sine_axiom_selection_previous) { create :sine_axiom_selection } let!(:proof_attempt_configuration_previous) do pac = proof_attempt.proof_attempt_configuration From b05274494d45dbc783ce3970573f7406c0ef1349 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 23 Oct 2015 15:54:54 +0200 Subject: [PATCH 011/240] Remove unused method initialize_axioms. --- app/fake_records/proof.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/fake_records/proof.rb b/app/fake_records/proof.rb index 54203f6cc..f777a3664 100644 --- a/app/fake_records/proof.rb +++ b/app/fake_records/proof.rb @@ -112,11 +112,6 @@ def normalize_check_box_ids(collection) collection.select(&:present?).map(&:to_i) if collection end - def initialize_axioms(opts) - axiom_ids = normalize_check_box_ids(opts[:proof][:axioms]) - @axioms = axiom_ids.map { |id| Axiom.find(id) } if axiom_ids - end - def initialize_axiom_selection(opts) @axiom_selection_method = opts[:proof][:axiom_selection_method].try(:to_sym) build_axiom_selection(opts) From 3d39252cc6a72c7ccfd3ea6edb50c0d29decdd47 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 09:01:32 +0200 Subject: [PATCH 012/240] Add spec for ProofExecutionWorker. --- spec/lib/proof_execution_worker_spec.rb | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spec/lib/proof_execution_worker_spec.rb diff --git a/spec/lib/proof_execution_worker_spec.rb b/spec/lib/proof_execution_worker_spec.rb new file mode 100644 index 000000000..227327c2d --- /dev/null +++ b/spec/lib/proof_execution_worker_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe ProofExecutionWorker do + let(:pew) { ProofExecutionWorker.new } + subject { pew } + + let!(:proof_attempt) { create :proof_attempt } + + context 'Methods' do + context 'perform calls a ProofExecution' do + before do + allow_any_instance_of(ProofExecution).to receive(:call) + allow(ProofExecution).to receive(:new).and_call_original + expect(ProofExecution).to receive(:new).once.with(proof_attempt) + end + + it 'works' do + subject.perform(proof_attempt.id) + end + end + + context 'perform creates a ProofExecution with a ProofAttempt object' do + before do + allow_any_instance_of(ProofExecution).to receive(:call) + allow(ProofExecution).to receive(:new).and_call_original + expect_any_instance_of(ProofExecution).to receive(:call).once + end + + it 'works' do + subject.perform(proof_attempt.id) + end + end + end +end From 4c77a4730a0f49a0c61423da62f60568652d3d2c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 09:45:55 +0200 Subject: [PATCH 013/240] Remove unused method prove_options_from_configuration. --- app/models/proof_attempt.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/models/proof_attempt.rb b/app/models/proof_attempt.rb index 8cca241c5..f33153321 100644 --- a/app/models/proof_attempt.rb +++ b/app/models/proof_attempt.rb @@ -73,12 +73,4 @@ def proper_subset_of_axioms_selected? available = theorem.ontology.axioms selected.any? && selected.count < available.count end - - protected - - def prove_options_from_configuration - pac = proof_attempt_configuration - Hets::ProveOptions.new({prover: pac.prover, - timeout: pac.timeout}) - end end From 2604e613770849504610feb21e05765daa640221 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 10:28:57 +0200 Subject: [PATCH 014/240] Add spec on SInE parameters. --- spec/lib/proof_spec.rb | 52 +++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/spec/lib/proof_spec.rb b/spec/lib/proof_spec.rb index 13b9e8ccd..2148d9d1c 100644 --- a/spec/lib/proof_spec.rb +++ b/spec/lib/proof_spec.rb @@ -172,23 +172,49 @@ end context 'axiom selection' do - let(:params_modified) do - theorem_params.merge({proof: params[:proof].merge({ - axioms: axioms.map(&:id) - })}) - end - let(:proof_modified) { Proof.new(params_modified) } + context 'SInE' do + let(:params_modified) do + theorem_params.merge({proof: params[:proof].merge({ + axiom_selection_method: 'sine_axiom_selection', + sine_axiom_selection: { + commonness_threshold: 0, + depth_limit: -1, + tolerance: 1 + } + })}) + end - before { proof_modified.save! } + let(:proof_modified) { Proof.new(params_modified) } - it 'is of the correct class' do - expect(proof_modified.axiom_selection.specific.class). - to eq(ManualAxiomSelection) + before { proof_modified.save! } + + it 'is of the correct class' do + expect(proof_modified.axiom_selection.specific.class). + to eq(SineAxiomSelection) + end end - it 'is correct' do - expect(proof_modified.axiom_selection.axioms). - to match_array(axioms) + context 'manual' do + let(:params_modified) do + theorem_params.merge({proof: params[:proof].merge({ + axioms: axioms.map(&:id) + })}) + end + + let(:proof_modified) { Proof.new(params_modified) } + + before { proof_modified.save! } + + it 'is of the correct class' do + expect(proof_modified.axiom_selection.specific.class). + to eq(ManualAxiomSelection) + end + + + it 'is correct' do + expect(proof_modified.axiom_selection.axioms). + to match_array(axioms) + end end end From fc7bf0c08840aef27850f66cd8eec46a54a9b5d9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 10:30:48 +0200 Subject: [PATCH 015/240] Put existing spec inside another context. --- spec/lib/proof_spec.rb | 74 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/spec/lib/proof_spec.rb b/spec/lib/proof_spec.rb index 2148d9d1c..ec4e0eb7f 100644 --- a/spec/lib/proof_spec.rb +++ b/spec/lib/proof_spec.rb @@ -219,52 +219,54 @@ end context 'on save!' do - before do - allow(ProofExecutionWorker).to receive(:perform_async) - end - - it 'with the proof attempts saved' do - proof.proof_attempts.each do |proof_attempt| - allow(proof_attempt).to receive(:save!).and_call_original - end - proof.save! - proof.proof_attempts.each do |proof_attempt| - expect(proof_attempt).to have_received(:save!) + context 'stubbed' do + before do + allow(ProofExecutionWorker).to receive(:perform_async) end - end - context 'ProofAttemptConfigurations' do - before { proof.save! } - - it 'are created' do + it 'with the proof attempts saved' do proof.proof_attempts.each do |proof_attempt| - expect(proof_attempt.reload.proof_attempt_configuration). - not_to be(nil) + allow(proof_attempt).to receive(:save!).and_call_original + end + proof.save! + proof.proof_attempts.each do |proof_attempt| + expect(proof_attempt).to have_received(:save!) end end - it 'have the provers set' do - expect(proof.proof_attempts. - map(&:reload). - map(&:proof_attempt_configuration). - map(&:prover).uniq).to match_array(provers) - end + context 'ProofAttemptConfigurations' do + before { proof.save! } - it 'have the timeout set' do - proof.proof_attempts.each do |proof_attempt| - expect(proof_attempt.reload.proof_attempt_configuration.timeout). - to eq(timeout) + it 'are created' do + proof.proof_attempts.each do |proof_attempt| + expect(proof_attempt.reload.proof_attempt_configuration). + not_to be(nil) + end end - end - end - it 'with the proof attempts saved' do - proof.proof_attempts.each do |proof_attempt| - allow(proof_attempt).to receive(:save!).and_call_original + it 'have the provers set' do + expect(proof.proof_attempts. + map(&:reload). + map(&:proof_attempt_configuration). + map(&:prover).uniq).to match_array(provers) + end + + it 'have the timeout set' do + proof.proof_attempts.each do |proof_attempt| + expect(proof_attempt.reload.proof_attempt_configuration.timeout). + to eq(timeout) + end + end end - proof.save! - proof.proof_attempts.each do |proof_attempt| - expect(proof_attempt).to have_received(:save!) + + it 'with the proof attempts saved' do + proof.proof_attempts.each do |proof_attempt| + allow(proof_attempt).to receive(:save!).and_call_original + end + proof.save! + proof.proof_attempts.each do |proof_attempt| + expect(proof_attempt).to have_received(:save!) + end end end end From 787311774d1800d0471af8f43da7a196a8adadc3 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 12:47:14 +0200 Subject: [PATCH 016/240] Add specs on importing errors. --- spec/lib/proof_spec.rb | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/spec/lib/proof_spec.rb b/spec/lib/proof_spec.rb index ec4e0eb7f..936a71d36 100644 --- a/spec/lib/proof_spec.rb +++ b/spec/lib/proof_spec.rb @@ -269,6 +269,47 @@ end end end + + context 'with an error' do + before do + # perform_async won't be executed in Sidekiq inline mode because it + # directly calls Redis. + allow(ProofExecutionWorker).to receive(:perform_async) do |*args| + ProofExecutionWorker.new.perform(*args) + end + end + + let(:params_modified) do + theorem_params.merge({proof: params[:proof].merge({ + prover_ids: [provers.first.id.to_s, ''] + })}) + end + let(:proof_modified) { Proof.new(params_modified) } + + context 'invalid JSON response' do + before do + allow(Hets).to receive(:prove_via_api). + and_return(StringIO.new('{"invalid_json": ]')) + end + + it 'raises an error' do + expect { proof_modified.save! }. + to raise_error(Hets::JSONParser::ParserError) + end + end + + context '"nothing to prove" response' do + before do + allow(Hets).to receive(:prove_via_api). + and_return(StringIO.new('nothing to prove')) + end + + it 'raises an error' do + expect { proof_modified.save! }. + to raise_error(Hets::Errors::HetsFileError) + end + end + end end end end From dbea725231edc85fbf41fa37344193f40a6c75a3 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 13:13:41 +0200 Subject: [PATCH 017/240] Remove unneeded method 'axiom' Since removing the default scope on Sentence, this is not needed any more. --- app/models/sine_symbol_axiom_trigger.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models/sine_symbol_axiom_trigger.rb b/app/models/sine_symbol_axiom_trigger.rb index 267d2716c..0fcf0c6d8 100644 --- a/app/models/sine_symbol_axiom_trigger.rb +++ b/app/models/sine_symbol_axiom_trigger.rb @@ -4,8 +4,4 @@ class SineSymbolAxiomTrigger < ActiveRecord::Base belongs_to :axiom_selection attr_accessible :tolerance - - def axiom - Axiom.unscoped.find(axiom_id) - end end From c5d86c43dd3b1fcd5d11eca04d5e44c28375d06c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 13:19:37 +0200 Subject: [PATCH 018/240] Simplify to_s definition. --- app/models/generated_axiom.rb | 4 +--- app/models/sentence.rb | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/models/generated_axiom.rb b/app/models/generated_axiom.rb index a25537714..d94808143 100644 --- a/app/models/generated_axiom.rb +++ b/app/models/generated_axiom.rb @@ -2,7 +2,5 @@ class GeneratedAxiom < ActiveRecord::Base belongs_to :proof_attempt attr_accessible :name - def to_s - name - end + alias_attribute :to_s, :name end diff --git a/app/models/sentence.rb b/app/models/sentence.rb index 3b5445c82..0e6f6f3be 100644 --- a/app/models/sentence.rb +++ b/app/models/sentence.rb @@ -10,6 +10,8 @@ class Sentence < ActiveRecord::Base attr_accessible :locid + alias_attribute :to_s, :name + delegate :repository, to: :ontology def self.find_with_locid(locid, _iri = nil) @@ -39,8 +41,4 @@ def hierarchical_class_names [] end end - - def to_s - name - end end From 3012f2f26fa865376639018283dec8ada44e0704 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 17:13:06 +0200 Subject: [PATCH 019/240] Alias methods instead of redefinition. --- app/models/proof_status.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/models/proof_status.rb b/app/models/proof_status.rb index 3ccb7dbba..65a80063a 100644 --- a/app/models/proof_status.rb +++ b/app/models/proof_status.rb @@ -17,6 +17,10 @@ class ProofStatus < ActiveRecord::Base :solved, :locid + alias_attribute :to_s, :identifier + alias_attribute :to_param, :identifier + alias_attribute :solved?, :solved + validates_presence_of :label before_create :generate_locid @@ -25,18 +29,6 @@ def self.find_with_locid(locid, _iri = nil) where(locid: locid).first end - def to_s - identifier - end - - def to_param - identifier - end - - def solved? - solved - end - protected def generate_locid From aaed3b8ba5283ffe20814ea45c6d148fd74eec92 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 17:18:31 +0200 Subject: [PATCH 020/240] Remove unused models: StructuredProof[Part]. --- app/models/structured_proof.rb | 3 -- app/models/structured_proof_part.rb | 6 ---- ...20151024151554_remove_structured_proofs.rb | 30 +++++++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) delete mode 100644 app/models/structured_proof.rb delete mode 100644 app/models/structured_proof_part.rb create mode 100644 db/migrate/20151024151554_remove_structured_proofs.rb diff --git a/app/models/structured_proof.rb b/app/models/structured_proof.rb deleted file mode 100644 index 3af6da7fd..000000000 --- a/app/models/structured_proof.rb +++ /dev/null @@ -1,3 +0,0 @@ -class StructuredProof < ActiveRecord::Base - attr_accessible :rule -end diff --git a/app/models/structured_proof_part.rb b/app/models/structured_proof_part.rb deleted file mode 100644 index 22061af78..000000000 --- a/app/models/structured_proof_part.rb +++ /dev/null @@ -1,6 +0,0 @@ -class StructuredProofPart < ActiveRecord::Base - belongs_to :structured_proof - belongs_to :sentence - belongs_to :mapping_version - # attr_accessible :title, :body -end diff --git a/db/migrate/20151024151554_remove_structured_proofs.rb b/db/migrate/20151024151554_remove_structured_proofs.rb new file mode 100644 index 000000000..9ef1fa02b --- /dev/null +++ b/db/migrate/20151024151554_remove_structured_proofs.rb @@ -0,0 +1,30 @@ +class RemoveStructuredProofs < ActiveRecord::Migration + def up + drop_table 'structured_proof_parts' + drop_table 'structured_proofs' + end + + def down + create_table 'structured_proof_parts', :force => true do |t| + t.integer 'structured_proof_id', :null => false + t.integer 'sentence_id', :null => false + t.integer 'mapping_version_id', :null => false + t.datetime 'created_at', :null => false + t.datetime 'updated_at', :null => false + end + + add_index 'structured_proof_parts', ['mapping_version_id'], :name => 'index_structured_proof_parts_on_mapping_version_id' + add_index 'structured_proof_parts', ['sentence_id'], :name => 'index_structured_proof_parts_on_sentence_id' + add_index 'structured_proof_parts', ['structured_proof_id'], :name => 'index_structured_proof_parts_on_structured_proof_id' + + create_table 'structured_proofs', :force => true do |t| + t.string 'rule' + t.datetime 'created_at', :null => false + t.datetime 'updated_at', :null => false + end + + add_foreign_key 'structured_proof_parts', 'mapping_versions', name: 'structured_proof_parts_mapping_version_id_fk', dependent: :delete + add_foreign_key 'structured_proof_parts', 'sentences', name: 'structured_proof_parts_sentence_id_fk', dependent: :delete + add_foreign_key 'structured_proof_parts', 'structured_proofs', name: 'structured_proof_parts_structured_proof_id_fk', dependent: :delete + end +end From e05cbe22aa467c191bb53305cf0c9454d0545d20 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 17:22:28 +0200 Subject: [PATCH 021/240] Remove unused model: UsedSentence. --- app/models/used_sentence.rb | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 app/models/used_sentence.rb diff --git a/app/models/used_sentence.rb b/app/models/used_sentence.rb deleted file mode 100644 index 004fd0c93..000000000 --- a/app/models/used_sentence.rb +++ /dev/null @@ -1,5 +0,0 @@ -class UsedSentence < ActiveRecord::Base - belongs_to :basic_proof - belongs_to :sentence - # attr_accessible :title, :body -end From 2954f7987986f2f2f405da05c725b1a9a32b2f3e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 24 Oct 2015 19:04:09 +0200 Subject: [PATCH 022/240] Remove unused JSON conversion methods. --- lib/hets/hets_options.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/hets/hets_options.rb b/lib/hets/hets_options.rb index d8a82ca93..9d79d9eaa 100644 --- a/lib/hets/hets_options.rb +++ b/lib/hets/hets_options.rb @@ -6,10 +6,6 @@ def self.from_hash(hash) new(hash['options']) end - def self.from_json(json) - from_hash(JSON.parse(json)) - end - def initialize(opts = {}) @options = opts.dup prepare @@ -25,10 +21,6 @@ def merge!(hets_options) add(hets_options.options) end - def to_json - {'options' => options}.to_json - end - def ==(other) options == other.options end From b0fe62b8fe39a55fb39e507f6652d5166e97cfd2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 09:51:19 +0100 Subject: [PATCH 023/240] Update gems: Sidekiq, Sidetiq Celluloid 17.2 is needed for eye. --- Gemfile | 4 ++-- Gemfile.lock | 45 +++++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index 5d8f77eba..0139dc453 100644 --- a/Gemfile +++ b/Gemfile @@ -89,8 +89,8 @@ gem 'acts_as_tree', '~> 2.2.0' gem "rest-client", '~> 1.8.0' # Background-Jobs -gem 'sidekiq', '~> 3.4.2' -gem 'sidetiq', '~> 0.6.3' +gem 'sidekiq', '~> 3.5.3' +gem 'sidetiq', github: 'PaulMest/sidetiq', ref: 'd88f9e483affcbadbd9e8b98b4a0a9518933887a' gem 'sidekiq-failures', '~> 0.4.5' gem 'sidekiq-status', '~> 0.5.4' gem 'sinatra', '~> 1.4.5', require: false, group: [:development, :production] diff --git a/Gemfile.lock b/Gemfile.lock index 8980d796b..814e54171 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,6 +12,16 @@ GIT libxml-ruby (~> 2.8.0) rails (~> 3.2.21) +GIT + remote: git://github.com/PaulMest/sidetiq.git + revision: d88f9e483affcbadbd9e8b98b4a0a9518933887a + ref: d88f9e483affcbadbd9e8b98b4a0a9518933887a + specs: + sidetiq (0.6.3) + celluloid (>= 0.14.1) + ice_cube (= 0.11.1) + sidekiq (>= 3.0.0) + GIT remote: git://github.com/dtaniwaki/rails_config.git revision: e07c116d5591d31584215ac3bccc50321eef23f2 @@ -111,8 +121,23 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - celluloid (0.16.0) - timers (~> 4.0.0) + celluloid (0.17.2) + celluloid-essentials + celluloid-extras + celluloid-fsm + celluloid-pool + celluloid-supervision + timers (>= 4.1.1) + celluloid-essentials (0.20.5) + timers (>= 4.1.1) + celluloid-extras (0.20.5) + timers (>= 4.1.1) + celluloid-fsm (0.20.5) + timers (>= 4.1.1) + celluloid-pool (0.20.5) + timers (>= 4.1.1) + celluloid-supervision (0.20.5) + timers (>= 4.1.1) choice (0.2.0) chunky_png (1.3.4) cliver (0.3.2) @@ -244,7 +269,7 @@ GEM hashie (3.4.2) highline (1.6.21) hike (1.2.3) - hitimes (1.2.2) + hitimes (1.2.3) htmlentities (4.3.4) http-cookie (1.0.2) domain_name (~> 0.5) @@ -453,8 +478,8 @@ GEM shoulda-matchers (2.8.0) activesupport (>= 3.0.0) shoulda_routing_macros (0.1.2) - sidekiq (3.4.2) - celluloid (~> 0.16.0) + sidekiq (3.5.3) + celluloid (~> 0.17.2) connection_pool (~> 2.2, >= 2.2.0) json (~> 1.0) redis (~> 3.2, >= 3.2.1) @@ -463,10 +488,6 @@ GEM sidekiq (>= 2.16.0) sidekiq-status (0.5.4) sidekiq (>= 2.7) - sidetiq (0.6.3) - celluloid (>= 0.14.1) - ice_cube (= 0.11.1) - sidekiq (>= 3.0.0) simple_form (2.1.3) actionpack (~> 3.0) activemodel (~> 3.0) @@ -502,7 +523,7 @@ GEM thread (0.2.2) thread_safe (0.3.5) tilt (1.4.1) - timers (4.0.1) + timers (4.1.1) hitimes tins (1.6.0) treetop (1.4.15) @@ -613,10 +634,10 @@ DEPENDENCIES secure_headers (~> 2.2.3) shoulda (~> 3.5.0) shoulda_routing_macros (~> 0.1.2) - sidekiq (~> 3.4.2) + sidekiq (~> 3.5.3) sidekiq-failures (~> 0.4.5) sidekiq-status (~> 0.5.4) - sidetiq (~> 0.6.3) + sidetiq! simple_form (~> 2.1) simplecov (~> 0.10.0) sinatra (~> 1.4.5) From 8709a5d8039c52e7c96c4600109163bc1e4a0416 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 09:54:59 +0100 Subject: [PATCH 024/240] Replace gem god by eye. --- Gemfile | 2 +- Gemfile.lock | 103 +++++++++++++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 46 deletions(-) diff --git a/Gemfile b/Gemfile index 0139dc453..14085a0b9 100644 --- a/Gemfile +++ b/Gemfile @@ -177,7 +177,7 @@ end group :production do # puma is __the only exception__ for which we don't specify a version. gem 'puma' - gem 'god', '~> 0.13.4' + gem 'eye', '~> 0.8.pre2' gem 'exception_notification', '~> 4.1.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 814e54171..9e1589170 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,7 +39,7 @@ GIT GIT remote: git://github.com/tristanm/jstree-rails.git - revision: e2ad211ea7835a1811e66c3d4ef1e21bfbe6e27d + revision: 66fbbb9ffc4ce8963cbd1717ef100864e7c5df1d specs: jstree-rails (0.0.1) rails (>= 3.1) @@ -84,7 +84,7 @@ GEM addressable (2.3.8) ansi (1.5.0) arel (3.0.3) - autoprefixer-rails (5.2.1.2) + autoprefixer-rails (6.1.0.1) execjs json bcrypt (3.1.10) @@ -109,7 +109,7 @@ GEM capistrano-bundler (1.1.4) capistrano (~> 3.1) sshkit (~> 1.2) - capistrano-rails (1.1.3) + capistrano-rails (1.1.5) capistrano (~> 3.1) capistrano-bundler (~> 1.1) capistrano-rvm (0.1.2) @@ -134,12 +134,16 @@ GEM timers (>= 4.1.1) celluloid-fsm (0.20.5) timers (>= 4.1.1) + celluloid-io (0.17.2) + celluloid (>= 0.17.2) + nio4r (>= 1.1) + timers (>= 4.1.1) celluloid-pool (0.20.5) timers (>= 4.1.1) celluloid-supervision (0.20.5) timers (>= 4.1.1) choice (0.2.0) - chunky_png (1.3.4) + chunky_png (1.3.5) cliver (0.3.2) coderay (1.1.0) coffee-rails (3.2.2) @@ -195,27 +199,27 @@ GEM warden (~> 1.2.3) diff-lcs (1.2.5) docile (1.1.5) - domain_name (0.5.24) + domain_name (0.5.25) unf (>= 0.0.5, < 1.0.0) dotenv (1.0.2) easy_translate (0.5.0) json thread thread_safe - elasticsearch (1.0.12) - elasticsearch-api (= 1.0.12) - elasticsearch-transport (= 1.0.12) - elasticsearch-api (1.0.12) + elasticsearch (1.0.14) + elasticsearch-api (= 1.0.14) + elasticsearch-transport (= 1.0.14) + elasticsearch-api (1.0.14) multi_json - elasticsearch-extensions (0.0.18) + elasticsearch-extensions (0.0.19) ansi ruby-prof - elasticsearch-model (0.1.7) + elasticsearch-model (0.1.8) activesupport (> 3) elasticsearch (> 0.4) hashie - elasticsearch-rails (0.1.7) - elasticsearch-transport (1.0.12) + elasticsearch-rails (0.1.8) + elasticsearch-transport (1.0.14) faraday multi_json em-proxy (0.1.8) @@ -226,6 +230,12 @@ GEM actionmailer (>= 3.0.4) activesupport (>= 3.0.4) execjs (2.6.0) + eye (0.8.pre2) + celluloid (~> 0.17.2) + celluloid-io (~> 0.17.0) + sigar (~> 0.7.3) + state_machine + thor facter (2.4.4) CFPropertyList (~> 2.2.6) factory_girl (4.5.0) @@ -235,7 +245,7 @@ GEM railties (>= 3.0.0) faker (1.5.0) i18n (~> 0.5) - faraday (0.9.1) + faraday (0.9.2) multipart-post (>= 1.2, < 3) ffi (1.9.10) font-awesome-sass (4.4.0) @@ -245,7 +255,6 @@ GEM formatador (0.2.5) gherkin (2.12.2) multi_json (~> 1.3) - god (0.13.6) haml (4.0.7) tilt haml-rails (0.4) @@ -266,8 +275,8 @@ GEM has_scope (0.6.0) actionpack (>= 3.2, < 5) activesupport (>= 3.2, < 5) - hashie (3.4.2) - highline (1.6.21) + hashie (3.4.3) + highline (1.7.8) hike (1.2.3) hitimes (1.2.3) htmlentities (4.3.4) @@ -275,13 +284,13 @@ GEM domain_name (~> 0.5) http-parser-lite (0.6.0) i18n (0.7.0) - i18n-tasks (0.8.6) - activesupport + i18n-tasks (0.8.7) + activesupport (>= 2.3.18) easy_translate (>= 0.5.0) erubis - highline + highline (>= 1.7.3) i18n - term-ansicolor + term-ansicolor (>= 1.3.2) terminal-table (>= 1.5.1) ice_cube (0.11.1) indefinite_article (0.2.4) @@ -303,7 +312,7 @@ GEM thor (~> 0.19) uuid (~> 2.3) journey (1.0.4) - jquery-rails (3.1.3) + jquery-rails (3.1.4) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) jquery-ui-rails (5.0.5) @@ -317,7 +326,7 @@ GEM activesupport (>= 3.0.0) launchy (2.4.3) addressable (~> 2.3) - libv8 (3.16.14.11) + libv8 (3.16.14.13) libxml-ruby (2.8.0) link_header (0.0.8) macaddr (1.7.1) @@ -331,30 +340,31 @@ GEM mini_portile (0.6.2) mocha (1.1.0) metaclass (~> 0.0.1) - momentjs-rails (2.10.3) + momentjs-rails (2.10.6) railties (>= 3.1) multi_json (1.11.2) multi_test (0.1.2) multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (2.9.2) - netrc (0.10.3) + net-ssh (3.0.1) + netrc (0.11.0) + nio4r (1.1.1) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) options (2.3.2) orm_adapter (0.5.0) - pg (0.18.2) + pg (0.18.3) poltergeist (1.6.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) websocket-driver (>= 0.2.0) polyglot (0.3.5) - progress_bar (1.0.3) - highline (~> 1.6.1) + progress_bar (1.0.5) + highline (~> 1.6) options (~> 2.3.0) - pry (0.10.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) @@ -363,11 +373,11 @@ GEM pry (~> 0.10) pry-rails (0.3.4) pry (>= 0.9.10) - puma (2.13.4) + puma (2.14.0) quiet_assets (1.1.0) railties (>= 3.1, < 5.0) rack (1.4.7) - rack-cache (1.2) + rack-cache (1.5.1) rack (>= 0.4) rack-mini-profiler (0.9.7) rack (>= 1.1.3) @@ -385,7 +395,7 @@ GEM activesupport (= 3.2.22) bundler (~> 1.0) railties (= 3.2.22) - rails-erd (1.4.2) + rails-erd (1.4.4) activerecord (>= 3.2) activesupport (>= 3.2) choice (~> 0.2.0) @@ -399,10 +409,10 @@ GEM thor (>= 0.14.6, < 2.0) rainbow (2.0.0) rake (10.4.2) - rb-fsevent (0.9.5) + rb-fsevent (0.9.6) rb-inotify (0.9.5) ffi (>= 0.5.0) - rdf (1.1.15) + rdf (1.1.17.1) link_header (~> 0.0, >= 0.0.8) rdf-aggregate-repo (1.1.0) rdf (>= 1.1) @@ -414,16 +424,16 @@ GEM rdf (~> 1.1, >= 1.1.6) rdf-aggregate-repo (~> 1.1) rdf-xsd (~> 1.1) - rdf-rdfxml (1.1.4) + rdf-rdfxml (1.1.5) htmlentities (~> 4.3) rdf (~> 1.1, >= 1.1.6) rdf-rdfa (~> 1.1, >= 1.1.4.1) rdf-xsd (~> 1.1) - rdf-xsd (1.1.4) + rdf-xsd (1.1.5) rdf (~> 1.1, >= 1.1.9) rdoc (3.12.2) json (~> 1.4) - redcarpet (3.3.2) + redcarpet (3.3.3) redis (3.2.1) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) @@ -436,7 +446,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) - rspec-activemodel-mocks (1.0.1) + rspec-activemodel-mocks (1.0.2) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) @@ -458,18 +468,19 @@ GEM rspec-core (~> 2.99.0) rspec-expectations (~> 2.99.0) rspec-mocks (~> 2.99.0) + rspec-support (3.3.0) ruby-graphviz (1.2.2) ruby-prof (0.15.8) rubydns (0.8.5) eventmachine (~> 1.0.0) - rugged (0.23.2) + rugged (0.23.3) safe_yaml (1.0.4) - sass (3.4.17) + sass (3.4.19) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - secure_headers (2.2.3) + secure_headers (2.2.4) user_agent_parser shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) @@ -488,6 +499,7 @@ GEM sidekiq (>= 2.16.0) sidekiq-status (0.5.4) sidekiq (>= 2.7) + sigar (0.7.3) simple_form (2.1.3) actionpack (~> 3.0) activemodel (~> 3.0) @@ -510,6 +522,7 @@ GEM colorize (>= 0.7.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) + state_machine (1.2.0) strip_attributes (1.7.1) activemodel (>= 3.0, < 5.0) systemu (2.6.5) @@ -529,7 +542,7 @@ GEM treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.44) + tzinfo (0.3.45) uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) @@ -537,7 +550,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.1) - user_agent_parser (2.2.0) + user_agent_parser (2.3.0) uuid (2.3.8) macaddr (~> 1.0) vcr (2.9.3) @@ -583,11 +596,11 @@ DEPENDENCIES elasticsearch-model (~> 0.1.4) elasticsearch-rails (~> 0.1.4) exception_notification (~> 4.1.0) + eye (~> 0.8.pre2) factory_girl_rails (~> 4.5.0) faker (~> 1.5.0) font-awesome-sass (~> 4.4.0) foreigner (~> 1.7.2) - god (~> 0.13.4) haml-rails (~> 0.4) hamlbars (~> 2.1.1) handlebars_assets (~> 0.20.1) From 8af3d6ea723152fbc0ddf18eecab9470c10c2a21 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 15:58:00 +0100 Subject: [PATCH 025/240] Remove god config. --- config/god/app.rb | 41 ------------------- config/god/hets_workers.rb | 33 ---------------- config/god/sidekiq_workers.rb | 23 ----------- config/god/watcher.rb | 74 ----------------------------------- 4 files changed, 171 deletions(-) delete mode 100644 config/god/app.rb delete mode 100644 config/god/hets_workers.rb delete mode 100644 config/god/sidekiq_workers.rb delete mode 100644 config/god/watcher.rb diff --git a/config/god/app.rb b/config/god/app.rb deleted file mode 100644 index 4fc9788b4..000000000 --- a/config/god/app.rb +++ /dev/null @@ -1,41 +0,0 @@ -require File.expand_path('../sidekiq_workers', __FILE__) -require File.expand_path('../hets_workers', __FILE__) -require File.expand_path('../../../lib/environment_light_with_hets.rb', __FILE__) - -RAILS_ROOT = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' - -# High load workers (except hets) are: sequential and priority_push. -HIGH_LOAD_WORKERS_COUNT = 2 - -God.pid_file_directory = File.join(RAILS_ROOT, 'tmp/pids/') - -SidekiqWorkers.configure do - if ENV['RAILS_ENV']=='production' - # one worker per core - Settings.hets.instance_urls.size.times.each do - watch 'hets', 1 - end - - # one worker for hets load balancing - watch 'hets_load_balancing', 1 - - # one worker for the default queue - watch 'default', 5 - - # one worker for the sequential queue - watch 'sequential', 1 - - watch 'priority_push', 1 - else - # one worker for all queues - watch %w(hets hets_load_balancing default sequential priority_push), 1 - end -end - -HetsWorkers.configure do - Settings.hets.instance_urls.each do |hets_url| - if hets_url.match(%r{\Ahttps?://(localhost|127.0.0.1|0.0.0.0|::1)}) - watch(URI(hets_url).port) - end - end -end diff --git a/config/god/hets_workers.rb b/config/god/hets_workers.rb deleted file mode 100644 index 2b8a5a6c6..000000000 --- a/config/god/hets_workers.rb +++ /dev/null @@ -1,33 +0,0 @@ -require File.expand_path('../watcher', __FILE__) - -class HetsWorkers < Watcher - def group - 'hets' - end - - def start_cmd(port) - load_hets_settings - options = hets_server_options.dup - options << "--listen=#{port}" if port - "exec nice #{hets_executable} --server #{options.join(' ')}" - end - - def pid_file - false - end - - protected - - def hets_server_options - Settings.hets.server_options - end - - def hets_executable - Settings.hets.executable_path - end - - def load_hets_settings - require File.join(File.dirname(__FILE__), '..', '..', 'lib', - 'environment_light_with_hets.rb') - end -end diff --git a/config/god/sidekiq_workers.rb b/config/god/sidekiq_workers.rb deleted file mode 100644 index 1466af1f9..000000000 --- a/config/god/sidekiq_workers.rb +++ /dev/null @@ -1,23 +0,0 @@ -require File.expand_path('../watcher', __FILE__) - -class SidekiqWorkers < Watcher - def group - 'sidekiq' - end - - def start_cmd(queues, concurrency) - queue_opt = Array(queues).map { |q| " -q '#{q}'" }.join - %W( - exec nice bin/sidekiq - -e production - -c #{concurrency} - --pidfile #{pid_file} - --logfile log/sidekiq.log - #{queue_opt} - ).join(' ') - end - - def pid_file - File.join(RAILS_ROOT, "tmp/pids/sidekiq-#{count}.pid") - end -end diff --git a/config/god/watcher.rb b/config/god/watcher.rb deleted file mode 100644 index f2133a22b..000000000 --- a/config/god/watcher.rb +++ /dev/null @@ -1,74 +0,0 @@ -class Watcher - RAILS_ROOT = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' - - attr_accessor :count - - def self.configure(&block) - new.instance_eval(&block) - end - - def initialize - self.count = 0 - end - - def group - end - - def start_cmd(*_args) - end - - def pid_file - end - - def watch(*args) - self.count += 1 - - God.watch do |w| - w.dir = RAILS_ROOT - w.group = group - w.name = "#{group}-#{@count}" - w.pid_file = pid_file if pid_file - - w.interval = 30.seconds - w.start = start_cmd(*args) - w.start_grace = 10.seconds - - # Restart if memory gets too high - w.transition(:up, :restart) do |on| - on.condition(:memory_usage) do |c| - c.above = 350.megabytes - c.times = 2 - end - end - - # Determine the state on startup - w.transition(:init, true => :up, false => :start ) do |on| - on.condition(:process_running) do |c| - c.running = true - end - end - - # Determine when process has finished starting - w.transition([:start, :restart], :up) do |on| - on.condition(:process_running) do |c| - c.running = true - c.interval = 5.seconds - end - - # Failsafe - on.condition(:tries) do |c| - c.times = 5 - c.transition = :start - c.interval = 5.seconds - end - end - - # Start if process is not running - w.transition(:up, :start) do |on| - on.condition(:process_running) do |c| - c.running = false - end - end - end - end -end From cb79743f740eb48d19ddd359895d190f8ec3563b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 11:26:19 +0100 Subject: [PATCH 026/240] Add eye config. --- config/processes/app.eye | 42 ++++++++++++++++++++++ config/processes/eye_methods.rb | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 config/processes/app.eye create mode 100644 config/processes/eye_methods.rb diff --git a/config/processes/app.eye b/config/processes/app.eye new file mode 100644 index 000000000..92868c8c4 --- /dev/null +++ b/config/processes/app.eye @@ -0,0 +1,42 @@ +require 'fileutils' +require File.expand_path('../../../lib/environment_light_with_hets.rb', __FILE__) +require File.expand_path('../eye_methods.rb', __FILE__) + +Eye.config do + logger "#{Rails.root}/log/eye.log" +end + +Eye.application :ontohub do + working_dir Rails.root.to_s + env 'RAILS_ENV' => Rails.env + env 'PID_DIR' => Rails.root.join('tmp', 'pids').to_s + + # Create PID dir + FileUtils.mkdir_p(env['PID_DIR']) + + group :sidekiq do + # one worker per configured hets instance + Settings.hets.instance_urls.each_with_index do |_url, index| + sidekiq_process self, :"sidekiq-hets-#{index}", 'hets', 1 + end + + # one worker for hets load balancing + sidekiq_process self, :'sidekiq-hets-load-balancing', 'hets_load_balancing', 1 + + # one worker for the default queue + sidekiq_process self, :'sidekiq-default', 'default', 5 + + # one worker for the sequential queue + sidekiq_process self, :'sidekiq-sequential', 'sequential', 1 + + sidekiq_process self, :'sidekiq-priority_push', 'priority_push', 1 + end + + group :hets do + Settings.hets.instance_urls.each do |url| + if url.match(%r{\Ahttps?://(localhost|127.0.0.1|0.0.0.0|::1)}) + hets_process self, URI(url).port + end + end + end +end diff --git a/config/processes/eye_methods.rb b/config/processes/eye_methods.rb new file mode 100644 index 000000000..98e5ee279 --- /dev/null +++ b/config/processes/eye_methods.rb @@ -0,0 +1,64 @@ +def sidekiq_process(proxy, name, queues, concurrency) + queues_param = Array(queues).map { |q| " -q '#{q}'" }.join + sidekiq_pid_file = "#{proxy.env['PID_DIR']}/sidekiq-#{name}.pid" + sidekiq_log_file = "log/#{name}.log" + sidekiq_start_command = %W(bin/sidekiq + -e #{proxy.env['RAILS_ENV']} + -c #{concurrency} + --pidfile #{sidekiq_pid_file} + --logfile #{sidekiq_log_file} + #{queues_param}).join(' ') + + proxy.process(name) do + start_command sidekiq_start_command + pid_file sidekiq_pid_file + stdall sidekiq_log_file + daemonize true + stop_signals [:USR1, 0, :TERM, 10.seconds, :KILL] + + # Give the process some time for startup. Booting Rails can take some time. + start_grace 10.seconds + + # Ensure the CPU is below 30% the last 5 times checked. + check :cpu, every: 30.seconds, below: 100, times: 5 + # Ensure that the used memory is below the limit the last 5 times checked. + check :memory, every: 30.seconds, below: 300.megabytes, times: 5 + end +end + +def hets_process(proxy, port) + hets_pid_file = "#{proxy.env['PID_DIR']}/hets-#{port}.pid" + hets_log_file = "log/hets-#{port}.log" + + proxy.process(:"hets-#{port}") do + start_command hets_start_command(port) + pid_file hets_pid_file + stdall hets_log_file + daemonize true + stop_signals [:TERM, 10.seconds, :KILL] + + # Give the process some time for startup. + start_grace 2.seconds + + # Ensure the CPU is below 30% the last 5 times checked. + # check :cpu, every: 30.seconds, below: 100, times: 5 + + # Ensure that the used memory is below the limit + # at least 2 out of the last 5 times checked. + # check :memory, every: 30.seconds, below: 3000.megabytes, times: [2, 5] + end +end + +def hets_start_command(port) + options = hets_server_options.dup + options << "--listen=#{port}" if port + "#{hets_executable} --server #{options.join(' ')}" +end + +def hets_server_options + Settings.hets.server_options +end + +def hets_executable + Settings.hets.executable_path +end From bb5ccee2c410506911f72773fba0412ca3c98cad Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 12:58:06 +0100 Subject: [PATCH 027/240] Add Eyefile With the Eyefile, it is possible to run "local eye" instead of the global eye from the rails root to manage processes: leye load leye restart hets leye quit --stop_all --- Eyefile | 1 + 1 file changed, 1 insertion(+) create mode 100644 Eyefile diff --git a/Eyefile b/Eyefile new file mode 100644 index 000000000..eb708778e --- /dev/null +++ b/Eyefile @@ -0,0 +1 @@ +Eye.load('config/processes/app.eye') From be2455a49b585889e3ca647195d12b819d1a6f2c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 15:37:33 +0100 Subject: [PATCH 028/240] Add puma to Eye config. --- config/processes/app.eye | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/config/processes/app.eye b/config/processes/app.eye index 92868c8c4..d515925c6 100644 --- a/config/processes/app.eye +++ b/config/processes/app.eye @@ -39,4 +39,30 @@ Eye.application :ontohub do end end end + + process :puma do + ctrl_socket = 'unix:///tmp/pumactl.sock' + home = Rails.root.join('..').to_s + + trigger :flapping, times: 10, within: 1.minute + + daemonize true + pid_file "#{env['PID_DIR']}/puma.pid" + stdall 'log/puma.log' + + start_command "#{home}/bin/puma -C config/puma.rb --control=#{ctrl_socket} --control-token=" + stop_command "#{home}/bin/pumactl --control-url=#{ctrl_socket} stop" + restart_command "#{home}/bin/pumactl --control-url=#{ctrl_socket} restart" + + # just sleep this until process get up status + # (maybe enough to puma soft restart) + restart_grace 10.seconds + + # Ensure the CPU is below 80% the last 3 times checked. + check :cpu, every: 30.seconds, below: 80, times: 3 + + # Ensure that the used memory is below the limit + # at least 3 out of the last 5 times checked. + check :memory, every: 30.seconds, below: 256.megabytes, times: [3,5] + end end From 28462f1a135da9312e6a6d7bd9b28f18934e0399 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 17:33:34 +0100 Subject: [PATCH 029/240] Be more generous with sidekiq's resources. --- config/processes/eye_methods.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/processes/eye_methods.rb b/config/processes/eye_methods.rb index 98e5ee279..76e0ddee6 100644 --- a/config/processes/eye_methods.rb +++ b/config/processes/eye_methods.rb @@ -19,10 +19,10 @@ def sidekiq_process(proxy, name, queues, concurrency) # Give the process some time for startup. Booting Rails can take some time. start_grace 10.seconds - # Ensure the CPU is below 30% the last 5 times checked. - check :cpu, every: 30.seconds, below: 100, times: 5 + # Ensure the CPU is below 100% the last 5 times checked. + check :cpu, every: 5.minutes, below: 100, times: 5 # Ensure that the used memory is below the limit the last 5 times checked. - check :memory, every: 30.seconds, below: 300.megabytes, times: 5 + check :memory, every: 5.minutes, below: 2048.megabytes, times: 5 end end From 2a097efd69a7c2f1b6bfd4567f2f0ab934d0232a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 19:42:50 +0100 Subject: [PATCH 030/240] Use transaction, remove cleanup method The cleanup is only useful in case of an ungraceful shutdown during the selection. This is the proper use case of a transaction. --- app/models/sine_axiom_selection.rb | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/app/models/sine_axiom_selection.rb b/app/models/sine_axiom_selection.rb index 76a7344b4..13f4036c4 100644 --- a/app/models/sine_axiom_selection.rb +++ b/app/models/sine_axiom_selection.rb @@ -23,10 +23,11 @@ class SineAxiomSelection < ActiveRecord::Base def call Semaphore.exclusively(lock_key) do unless finished - cleanup - preprocess unless other_finished_sine_axiom_selections.any? - select_axioms - mark_as_finished! + transaction do + preprocess unless other_finished_sine_axiom_selections.any? + select_axioms + mark_as_finished! + end end end end @@ -43,18 +44,8 @@ def sine_symbol_axiom_triggers SineSymbolAxiomTrigger.where(axiom_selection_id: axiom_selection) end - def destroy - cleanup - super - end - protected - def cleanup - sine_symbol_commonnesses.each(&:destroy) - sine_symbol_axiom_triggers.each(&:destroy) - end - def preprocess calculate_commonness_table calculate_symbol_axiom_trigger_table From 2c131628d69e02c95aa9edae5a55ad49a2b05ec2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 20:17:12 +0100 Subject: [PATCH 031/240] Add sidekiq status web interface. --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 4c930fa91..c565a02f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ require 'sidekiq/web' if defined? Sidekiq +require 'sidekiq-status/web' if defined? Sidekiq::Status require Rails.root.join('lib', 'router_constraints.rb') Specroutes.define(Ontohub::Application.routes) do From b9fc71616da2eccbe52d2dbc31d63f6c2d46a864 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 6 Nov 2015 20:38:28 +0100 Subject: [PATCH 032/240] Unschedule force-freeing for completed jobs. --- app/models/hets_instance.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/hets_instance.rb b/app/models/hets_instance.rb index 00b91f7f3..57e21c552 100644 --- a/app/models/hets_instance.rb +++ b/app/models/hets_instance.rb @@ -95,6 +95,7 @@ def to_s def finish_work! reload + Sidekiq::Status.unschedule(@force_free_job_id) self.queue_size -= 1 if queue_size > 0 if queue_size > 0 set_busy! @@ -118,7 +119,8 @@ def set_force_free! def set_busy! self.state = 'busy' - HetsInstanceForceFreeWorker.perform_in(FORCE_FREE_WAITING_PERIOD, id) + @force_free_job_id = + HetsInstanceForceFreeWorker.perform_in(FORCE_FREE_WAITING_PERIOD, id) save! end From c25c5837cfb18cecfa078572b3c045a1f09467b5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 7 Nov 2015 00:34:23 +0100 Subject: [PATCH 033/240] Add spec on re-checking the up-state. --- spec/models/hets_instance_spec.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/models/hets_instance_spec.rb b/spec/models/hets_instance_spec.rb index 41c864833..c4c7e37c9 100644 --- a/spec/models/hets_instance_spec.rb +++ b/spec/models/hets_instance_spec.rb @@ -47,6 +47,32 @@ expect { HetsInstance.choose! }. to raise_error(HetsInstance::NoSelectableHetsInstanceError) end + + context 'try again re-checks up-state' do + before do + allow(HetsInstance).to receive(:choose!).and_call_original + allow_any_instance_of(HetsInstance). + to receive(:set_up_state!).and_call_original + expect_any_instance_of(HetsInstance). + to receive(:set_up_state!).exactly(HetsInstance.count).times + begin + HetsInstance.choose! + rescue HetsInstance::NoSelectableHetsInstanceError + end + end + + it 'should have tried again' do + expect(HetsInstance).to have_received(:choose!).with(no_args) + end + + it 'should have not tried a second time' do + expect(HetsInstance).to have_received(:choose!).with(try_again: false) + end + + it 'should have tried twice' do + expect(HetsInstance).to have_received(:choose!).twice + end + end end context 'and there is an acceptable hets instance' do From 0315cf613576d61eff8c0b4f59c08547f861e282 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 7 Nov 2015 00:34:33 +0100 Subject: [PATCH 034/240] Re-check the up-state. --- app/models/hets_instance.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/models/hets_instance.rb b/app/models/hets_instance.rb index 00b91f7f3..15972aa4a 100644 --- a/app/models/hets_instance.rb +++ b/app/models/hets_instance.rb @@ -60,13 +60,21 @@ def self.with_instance! result end - def self.choose! + def self.choose!(try_again: true) raise NoRegisteredHetsInstanceError.new unless any? + instance = nil Semaphore.exclusively(MUTEX_KEY) do instance = active.free.first instance ||= increment_queue! { active.force_free.load_balancing_order.first } instance ||= increment_queue! { active.busy.load_balancing_order.first } instance.try(:set_busy!) + end + if instance + instance + elsif try_again + find_each { |hets_instance| hets_instance.send(:set_up_state!) } + choose!(try_again: false) + else instance or raise NoSelectableHetsInstanceError.new end end From 32d8a9b661c67143b1b33e2808bb31d6d9df2767 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 7 Nov 2015 00:41:53 +0100 Subject: [PATCH 035/240] Add reasonable Hets response check. --- config/processes/eye_methods.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/config/processes/eye_methods.rb b/config/processes/eye_methods.rb index 76e0ddee6..7199390ad 100644 --- a/config/processes/eye_methods.rb +++ b/config/processes/eye_methods.rb @@ -40,12 +40,13 @@ def hets_process(proxy, port) # Give the process some time for startup. start_grace 2.seconds - # Ensure the CPU is below 30% the last 5 times checked. - # check :cpu, every: 30.seconds, below: 100, times: 5 - - # Ensure that the used memory is below the limit - # at least 2 out of the last 5 times checked. - # check :memory, every: 30.seconds, below: 3000.megabytes, times: [2, 5] + # Ensure that Hets responds + check :http, + url: "http://localhost:#{port}/version", + pattern: /\Av0.99, \d+\z/, + every: 30.seconds, + times: 1, + timeout: 2.seconds end end From 5473f39f9b7ac5f9e84cc80acedc682f30c0f4ff Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 8 Nov 2015 17:58:32 +0100 Subject: [PATCH 036/240] Add comment to sidetiq gem. --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 14085a0b9..46dc1c503 100644 --- a/Gemfile +++ b/Gemfile @@ -90,6 +90,8 @@ gem "rest-client", '~> 1.8.0' # Background-Jobs gem 'sidekiq', '~> 3.5.3' +# Originally, sidetiq is not compatible to celluloid 0.17.2. This fork fixes it. +# The ref is given to ensure that no other (possibly breaking) changes are taken. gem 'sidetiq', github: 'PaulMest/sidetiq', ref: 'd88f9e483affcbadbd9e8b98b4a0a9518933887a' gem 'sidekiq-failures', '~> 0.4.5' gem 'sidekiq-status', '~> 0.5.4' From 52c75654440f04cf2c12f4b7486e71698ae79d09 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:39:39 +0100 Subject: [PATCH 037/240] Fix comments. --- Gemfile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 46dc1c503..abc026893 100644 --- a/Gemfile +++ b/Gemfile @@ -21,14 +21,14 @@ gem 'redis-semaphore', '~> 0.2.4' # in production environments by default. group :assets do gem 'jstree-rails', github: 'tristanm/jstree-rails' - # sass-rails >= 4.0 is not compativle with rails 3 + # sass-rails >= 4.0 is not compativle to rails 3 gem 'sass-rails', '~> 3.2.6' gem 'bootstrap-sass', '~> 3.3.3' - # coffee-rails > 3.2 is not compatible with rails 3 + # coffee-rails > 3.2 is not compatible to rails 3 gem 'coffee-rails', '~> 3.2.2' gem 'compass', '~> 1.0.3' gem 'font-awesome-sass', '~> 4.4.0' - # jquery-rails > 3.1 is not compatible with rails 3 + # jquery-rails > 3.1 is not compatible to rails 3 gem 'jquery-rails', '~> 3.1.3' gem 'jquery-ui-rails', '~> 5.0.5' gem 'momentjs-rails', '~> 2.10.2' @@ -41,7 +41,7 @@ group :assets do gem 'bootstrap-select-rails', '~> 1.6.3' end -# Newer versions than 0.4 are not compatible with rails 3.2 +# Newer versions than 0.4 are not compatible to rails 3.2 gem 'haml-rails', '~> 0.4' # Project configuration @@ -50,11 +50,11 @@ gem 'haml-rails', '~> 0.4' # https://github.com/railsconfig/rails_config/pull/103 gem 'rails_config', github: 'dtaniwaki/rails_config', ref: 'merge-array-option' -#provides correct indefinite article +# Provides correct indefinite article gem 'indefinite_article', '~> 0.2.0' # Fancy Forms -# simple_form >= 3.0 is not compatible with rails 3 +# simple_form >= 3.0 is not compatible to rails 3 gem 'simple_form', '~> 2.1' # Inherited Resources @@ -121,7 +121,7 @@ gem 'specroutes', github: '0robustus1/specroutes' # gem 'ancestry' # Use dagnabit to model categories -# Newer versions than 3.0.x are not compatible with rails 3.2 +# Newer versions than 3.0.x are not compatible to rails 3.2 gem 'dagnabit', '~> 3.0.1' # Migrate data in separate tasks From b7ce7a5df2e7acd2f2a25b0815870751c346235f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:54:29 +0100 Subject: [PATCH 038/240] Add incompatibility comment. --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index abc026893..e5508ac1c 100644 --- a/Gemfile +++ b/Gemfile @@ -168,6 +168,7 @@ group :development, :test do gem 'rspec-rails', '~> 2.0' gem 'better_errors', '~> 2.1.1' gem 'binding_of_caller', '~> 0.7.2' + # i18n-tasks >= 0.9.0 is not compatible to rails 3/rdf gem 'i18n-tasks', '~> 0.8.3' gem 'pry-byebug', '~> 3.2.0' From 67cc4a4d9e3c9e86b113ea8c3b11ff6d2d292d98 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:35:54 +0100 Subject: [PATCH 039/240] Update gem acts_as_tree to 2.3.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index e5508ac1c..c9d9332dd 100644 --- a/Gemfile +++ b/Gemfile @@ -83,7 +83,7 @@ gem 'kaminari', '~> 0.16.1' gem "strip_attributes", "~> 1.0" # For distributed ontologies -gem 'acts_as_tree', '~> 2.2.0' +gem 'acts_as_tree', '~> 2.3.0' # HTTP Client gem "rest-client", '~> 1.8.0' From b3bef7e26f5bdb3bf7b33dd3a804170c68518473 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:36:01 +0100 Subject: [PATCH 040/240] Update gem database_cleaner to 1.5.1. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c9d9332dd..c9ab7330f 100644 --- a/Gemfile +++ b/Gemfile @@ -131,7 +131,7 @@ gem 'data_migrate', '~> 1.2.0' gem 'acts_as_relation', '~> 0.1.3' # Clean the database - especially needed in the seeds -gem 'database_cleaner', '~> 1.4.1' +gem 'database_cleaner', '~> 1.5.1' group :test do gem 'mocha', '~> 1.1.0', require: false From 5340b43bc256d75f69412e4852d3ed1018439d5b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:38:21 +0100 Subject: [PATCH 041/240] Update gem secure_headers to 2.4.3 The "-src" values now need to be set separately. They won't be set implicitly by the gem. --- Gemfile | 2 +- config/initializers/security_headers.rb | 10 +++++++++- spec/controllers/home_controller_spec.rb | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index c9ab7330f..f9c8e2c7d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'rails', '~> 3.2.22' gem 'rack-protection', '~> 1.5.3' -gem 'secure_headers', '~> 2.2.3' +gem 'secure_headers', '~> 2.4.3' gem 'rack-mini-profiler', require: false diff --git a/config/initializers/security_headers.rb b/config/initializers/security_headers.rb index 0e43e786b..aa4afa951 100644 --- a/config/initializers/security_headers.rb +++ b/config/initializers/security_headers.rb @@ -6,8 +6,16 @@ # By default, load resources only from own origin. # For CSS, allow styles from style elements and attributes for GWT. config.csp = { - default_src: "self", + default_src: "'self'", style_src: "'self' 'unsafe-inline'", + script_src: "'self'", + frame_src: "'self'", + img_src: "'self'", + connect_src: "'self'", + font_src: "'self'", + media_src: "'self'", + object_src: "'self'", + child_src: "'self'", disable_chrome_extension: true, } end diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb index 7cbd63d80..637ac000b 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/home_controller_spec.rb @@ -11,6 +11,7 @@ subject{ response.headers["Content-Security-Policy-Report-Only"] } it{ should include "style-src 'self' 'unsafe-inline';" } it{ should include "script-src 'self';" } + it{ should include "default-src 'self';" } end end From f6347817e7010a0cb5a13dd1156ad56be8ee777d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:40:53 +0100 Subject: [PATCH 042/240] Update gem capybara to 2.5.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f9c8e2c7d..2e8ba3fb6 100644 --- a/Gemfile +++ b/Gemfile @@ -143,7 +143,7 @@ group :test do gem "factory_girl_rails", '~> 4.5.0' # Required for integration tests - gem 'capybara', '~> 2.4.4' + gem 'capybara', '~> 2.5.0' gem 'poltergeist', '~> 1.6.0' gem 'launchy', '~> 2.4.3' From d57764724e0a34a9c22ebb8145092128d92b866c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:43:49 +0100 Subject: [PATCH 043/240] Update gem poltergeist to 1.7.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 2e8ba3fb6..974d4e0d4 100644 --- a/Gemfile +++ b/Gemfile @@ -144,7 +144,7 @@ group :test do # Required for integration tests gem 'capybara', '~> 2.5.0' - gem 'poltergeist', '~> 1.6.0' + gem 'poltergeist', '~> 1.7.0' gem 'launchy', '~> 2.4.3' gem 'cucumber-rails', '~> 1.4', require: false From e45ffa868e46b3eba0104f9b18b886252174efc7 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:44:09 +0100 Subject: [PATCH 044/240] Update gem pry-byebug to 3.3.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 974d4e0d4..3ae3df896 100644 --- a/Gemfile +++ b/Gemfile @@ -170,7 +170,7 @@ group :development, :test do gem 'binding_of_caller', '~> 0.7.2' # i18n-tasks >= 0.9.0 is not compatible to rails 3/rdf gem 'i18n-tasks', '~> 0.8.3' - gem 'pry-byebug', '~> 3.2.0' + gem 'pry-byebug', '~> 3.3.0' # Recording of HTTP Requests gem "vcr", '~> 2.9.3', require: false From 90414d49888db6130f354876ab9ea690e984565f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:45:05 +0100 Subject: [PATCH 045/240] Update gem webmock to 1.22.3. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3ae3df896..0470e8035 100644 --- a/Gemfile +++ b/Gemfile @@ -174,7 +174,7 @@ group :development, :test do # Recording of HTTP Requests gem "vcr", '~> 2.9.3', require: false - gem "webmock", '~> 1.21.0', require: false + gem "webmock", '~> 1.22.3', require: false end group :production do From 7c2ba5126a7548ead790b4eb762bcbbd83d3cb6e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:45:26 +0100 Subject: [PATCH 046/240] Update gem vcr to 3.0.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 0470e8035..d67738949 100644 --- a/Gemfile +++ b/Gemfile @@ -173,7 +173,7 @@ group :development, :test do gem 'pry-byebug', '~> 3.3.0' # Recording of HTTP Requests - gem "vcr", '~> 2.9.3', require: false + gem "vcr", '~> 3.0.0', require: false gem "webmock", '~> 1.22.3', require: false end From 0e99a849b80f418206289c6e2b6990042e59310e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:48:21 +0100 Subject: [PATCH 047/240] Update gem handlebars_assets to 0.21.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index d67738949..00c4a86cc 100644 --- a/Gemfile +++ b/Gemfile @@ -35,7 +35,7 @@ group :assets do gem 'd3_rails', '~> 3.5.6' gem 'therubyracer', '~> 0.12.1' gem 'uglifier', '>= 1.0.3' - gem 'handlebars_assets', '~> 0.20.1' + gem 'handlebars_assets', '~> 0.21.0' gem 'hamlbars', '~> 2.1.1' gem 'underscore-rails', '~> 1.8.2' gem 'bootstrap-select-rails', '~> 1.6.3' From fe7cd8c0e447e52057567858b795b33def40baee Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:58:38 +0100 Subject: [PATCH 048/240] Update gems rdf, rdf-n3 to 1.99.0. --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 00c4a86cc..4990ff0c2 100644 --- a/Gemfile +++ b/Gemfile @@ -11,9 +11,9 @@ gem 'pry-rails', '~> 0.3.2' gem 'pg', '~> 0.18.1' gem 'foreigner', '~> 1.7.2' -gem 'rdf', '~> 1.1.15' +gem 'rdf', '~> 1.99.0' gem 'rdf-rdfxml', '~> 1.1.3' -gem 'rdf-n3', '~> 1.1.2' +gem 'rdf-n3', '~> 1.99.0' gem 'redis-semaphore', '~> 0.2.4' From bc771a88eef12736aad8c0367eb7f6997467b88b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:40:34 +0100 Subject: [PATCH 049/240] Update puma. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9e1589170..7baea354e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -373,7 +373,7 @@ GEM pry (~> 0.10) pry-rails (0.3.4) pry (>= 0.9.10) - puma (2.14.0) + puma (2.15.3) quiet_assets (1.1.0) railties (>= 3.1, < 5.0) rack (1.4.7) From 61924d4843347a2f71033443934856b0ee0ccc5d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 13:58:48 +0100 Subject: [PATCH 050/240] Bundle update. --- Gemfile.lock | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7baea354e..4d030ca9d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,7 +79,7 @@ GEM multi_json (~> 1.0) acts_as_relation (0.1.3) activerecord (~> 3.2) - acts_as_tree (2.2.0) + acts_as_tree (2.3.0) activerecord (>= 3.0.0) addressable (2.3.8) ansi (1.5.0) @@ -99,8 +99,7 @@ GEM sass (>= 3.3.0) bootstrap-select-rails (1.6.3) builder (3.0.4) - byebug (5.0.0) - columnize (= 0.9.0) + byebug (8.0.1) cancan (1.6.10) capistrano (3.4.0) i18n @@ -115,7 +114,7 @@ GEM capistrano-rvm (0.1.2) capistrano (~> 3.0) sshkit (~> 1.2) - capybara (2.4.4) + capybara (2.5.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -154,7 +153,6 @@ GEM execjs coffee-script-source (1.9.1.1) colorize (0.7.7) - columnize (0.9.0) compass (1.0.3) chunky_png (~> 1.2) compass-core (~> 1.0.2) @@ -188,7 +186,7 @@ GEM activerecord (>= 2.3.0) data_migrate (1.2.0) rails (>= 3.0.0) - database_cleaner (1.4.1) + database_cleaner (1.5.1) debug_inspector (0.0.2) devise (3.5.2) bcrypt (~> 3.0) @@ -267,7 +265,7 @@ GEM haml sprockets (>= 2.0) tilt - handlebars_assets (0.20.2) + handlebars_assets (0.21.0) execjs (~> 2.0) multi_json (~> 1.0) sprockets (>= 2.0.0, < 4.0) @@ -275,6 +273,7 @@ GEM has_scope (0.6.0) actionpack (>= 3.2, < 5) activesupport (>= 3.2, < 5) + hashdiff (0.2.3) hashie (3.4.3) highline (1.7.8) hike (1.2.3) @@ -355,7 +354,7 @@ GEM options (2.3.2) orm_adapter (0.5.0) pg (0.18.3) - poltergeist (1.6.0) + poltergeist (1.7.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -368,8 +367,8 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - pry-byebug (3.2.0) - byebug (~> 5.0) + pry-byebug (3.3.0) + byebug (~> 8.0) pry (~> 0.10) pry-rails (0.3.4) pry (>= 0.9.10) @@ -412,16 +411,16 @@ GEM rb-fsevent (0.9.6) rb-inotify (0.9.5) ffi (>= 0.5.0) - rdf (1.1.17.1) + rdf (1.99.0) link_header (~> 0.0, >= 0.0.8) - rdf-aggregate-repo (1.1.0) - rdf (>= 1.1) - rdf-n3 (1.1.3) - rdf (~> 1.1, >= 1.1.5) - rdf-rdfa (1.1.6) + rdf-aggregate-repo (1.99.0) + rdf (~> 1.99) + rdf-n3 (1.99.0) + rdf (~> 1.99) + rdf-rdfa (1.99.0) haml (~> 4.0) htmlentities (~> 4.3) - rdf (~> 1.1, >= 1.1.6) + rdf (~> 1.99) rdf-aggregate-repo (~> 1.1) rdf-xsd (~> 1.1) rdf-rdfxml (1.1.5) @@ -480,7 +479,7 @@ GEM railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - secure_headers (2.2.4) + secure_headers (2.4.3) user_agent_parser shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) @@ -553,13 +552,14 @@ GEM user_agent_parser (2.3.0) uuid (2.3.8) macaddr (~> 1.0) - vcr (2.9.3) + vcr (3.0.0) warden (1.2.3) rack (>= 1.0) - webmock (1.21.0) + webmock (1.22.3) addressable (>= 2.3.6) crack (>= 0.3.2) - websocket-driver (0.6.2) + hashdiff + websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) xpath (2.0.0) @@ -572,7 +572,7 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.9.3) acts_as_relation (~> 0.1.3) - acts_as_tree (~> 2.2.0) + acts_as_tree (~> 2.3.0) better_errors (~> 2.1.1) binding_of_caller (~> 0.7.2) bootstrap-sass (~> 3.3.3) @@ -581,7 +581,7 @@ DEPENDENCIES capistrano (~> 3.4.0) capistrano-rails (~> 1.1.1) capistrano-rvm (~> 0.1.1) - capybara (~> 2.4.4) + capybara (~> 2.5.0) codemirror-rails! coffee-rails (~> 3.2.2) compass (~> 1.0.3) @@ -589,7 +589,7 @@ DEPENDENCIES d3_rails (~> 3.5.6) dagnabit (~> 3.0.1) data_migrate (~> 1.2.0) - database_cleaner (~> 1.4.1) + database_cleaner (~> 1.5.1) devise (~> 3.5.2) elasticsearch (~> 1.0.4) elasticsearch-extensions (~> 0.0.15) @@ -603,7 +603,7 @@ DEPENDENCIES foreigner (~> 1.7.2) haml-rails (~> 0.4) hamlbars (~> 2.1.1) - handlebars_assets (~> 0.20.1) + handlebars_assets (~> 0.21.0) has_scope (~> 0.6.0.rc) i18n-tasks (~> 0.8.3) indefinite_article (~> 0.2.0) @@ -621,9 +621,9 @@ DEPENDENCIES nokogiri (~> 1.6.6.2) ontology-united! pg (~> 0.18.1) - poltergeist (~> 1.6.0) + poltergeist (~> 1.7.0) progress_bar (~> 1.0.2) - pry-byebug (~> 3.2.0) + pry-byebug (~> 3.3.0) pry-rails (~> 0.3.2) puma quiet_assets (~> 1.1.0) @@ -632,8 +632,8 @@ DEPENDENCIES rails (~> 3.2.22) rails-erd (~> 1.4.2) rails_config! - rdf (~> 1.1.15) - rdf-n3 (~> 1.1.2) + rdf (~> 1.99.0) + rdf-n3 (~> 1.99.0) rdf-rdfxml (~> 1.1.3) redcarpet (~> 3.3.2) redis-semaphore (~> 0.2.4) @@ -644,7 +644,7 @@ DEPENDENCIES ruby-graphviz (~> 1.2.2) rugged (~> 0.23.2) sass-rails (~> 3.2.6) - secure_headers (~> 2.2.3) + secure_headers (~> 2.4.3) shoulda (~> 3.5.0) shoulda_routing_macros (~> 0.1.2) sidekiq (~> 3.5.3) @@ -659,8 +659,8 @@ DEPENDENCIES therubyracer (~> 0.12.1) uglifier (>= 1.0.3) underscore-rails (~> 1.8.2) - vcr (~> 2.9.3) - webmock (~> 1.21.0) + vcr (~> 3.0.0) + webmock (~> 1.22.3) yard (~> 0.8.7.6) BUNDLED WITH From 75db19b590073e812c7e3c7ca40a02ef91c13d63 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 14:55:26 +0100 Subject: [PATCH 051/240] Reduce scheduled TimeoutWorkers The timeout is only needed to cancel parsing jobs that take longer than the configured amount of time. Once the job is done, the TimeoutWorker job can be unscheduled. --- lib/ontology_batch_parse_worker.rb | 3 ++- lib/worker.rb | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ontology_batch_parse_worker.rb b/lib/ontology_batch_parse_worker.rb index 8258111b6..e61c21f41 100644 --- a/lib/ontology_batch_parse_worker.rb +++ b/lib/ontology_batch_parse_worker.rb @@ -22,7 +22,7 @@ def execute_perform(try_count, versions) return if versions.empty? version_id, opts = versions.head - TimeoutWorker.start_timeout_clock(version_id) + timeout_job_id = TimeoutWorker.start_timeout_clock(version_id) version = OntologyVersion.find(version_id) @@ -31,6 +31,7 @@ def execute_perform(try_count, versions) end version.parse + Sidekiq::Status.unschedule(timeout_job_id) rescue ConcurrencyBalancer::AlreadyProcessingError done = handle_concurrency_issue ensure diff --git a/lib/worker.rb b/lib/worker.rb index cd6b11f0c..43e76575b 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -20,8 +20,11 @@ def execute_perform(try_count, type, clazz, method, *args) when 'record' id = args.shift klass = clazz.constantize - TimeoutWorker.start_timeout_clock(id) if klass == OntologyVersion + if klass == OntologyVersion + timeout_job_id = TimeoutWorker.start_timeout_clock(id) + end klass.unscoped.find(id).send method, *args + Sidekiq::Status.unschedule(timeout_job_id) if timeout_job_id else raise ArgumentError, "unsupported type: #{type}" end From 5e030779a41cf8d2c57e879987f9ef3d4ab20480 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 9 Nov 2015 15:28:32 +0100 Subject: [PATCH 052/240] Remove unused constant. --- lib/timeout_worker.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/timeout_worker.rb b/lib/timeout_worker.rb index 3a056b689..b8cc0a539 100644 --- a/lib/timeout_worker.rb +++ b/lib/timeout_worker.rb @@ -2,8 +2,6 @@ class TimeoutWorker < BaseWorker class Error < ::StandardError; end class TimeOutNotSetError < Error; end - RESCHEDULE_TIME = 30 # minutes - sidekiq_options queue: 'default' def self.start_timeout_clock(ontology_version_id, hours_offset=nil) From 2d28d4e17a902fe835e68be41f2f6de4f147cd27 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 10 Nov 2015 13:22:52 +0100 Subject: [PATCH 053/240] Fix proving HTTP timeout The timeout for the prover is not the same as for the HTTP session. The HTTP session can take much longer because Hets analyses the ontology again for proving. This can take much longer than the prover timeout. --- lib/hets/prove_caller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/hets/prove_caller.rb b/lib/hets/prove_caller.rb index 8fbb29beb..71771ea53 100644 --- a/lib/hets/prove_caller.rb +++ b/lib/hets/prove_caller.rb @@ -23,8 +23,8 @@ def build_query_string end def timeout - timeout = hets_options.options['timeout'] - 30.seconds + 2 * timeout.to_i if timeout + # The HTTP timeout must be as long as Hets maximally takes. + HetsInstance::FORCE_FREE_WAITING_PERIOD end end end From 7bd2a3e3d8f731b1446a0bff059e6a31adcdd6c5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 11 Nov 2015 12:39:39 +0100 Subject: [PATCH 054/240] Unschedule the jobs on error as well. --- lib/ontology_batch_parse_worker.rb | 2 +- lib/worker.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ontology_batch_parse_worker.rb b/lib/ontology_batch_parse_worker.rb index e61c21f41..eccf58fca 100644 --- a/lib/ontology_batch_parse_worker.rb +++ b/lib/ontology_batch_parse_worker.rb @@ -31,10 +31,10 @@ def execute_perform(try_count, versions) end version.parse - Sidekiq::Status.unschedule(timeout_job_id) rescue ConcurrencyBalancer::AlreadyProcessingError done = handle_concurrency_issue ensure + Sidekiq::Status.unschedule(timeout_job_id) if timeout_job_id self.class.perform_async_with_priority(@queue, versions.tail, try_count: try_count) unless versions.tail.empty? || done end diff --git a/lib/worker.rb b/lib/worker.rb index 43e76575b..08a22ee52 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -24,12 +24,13 @@ def execute_perform(try_count, type, clazz, method, *args) timeout_job_id = TimeoutWorker.start_timeout_clock(id) end klass.unscoped.find(id).send method, *args - Sidekiq::Status.unschedule(timeout_job_id) if timeout_job_id else raise ArgumentError, "unsupported type: #{type}" end rescue ConcurrencyBalancer::AlreadyProcessingError handle_concurrency_issue + ensure + Sidekiq::Status.unschedule(timeout_job_id) if timeout_job_id end def handle_concurrency_issue From adce935530576d0f3e89dc339c6904b0168b34fb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 16 Nov 2015 08:37:53 +0100 Subject: [PATCH 055/240] Update gem poltergeist to 1.8.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 4990ff0c2..b5e4b2ebb 100644 --- a/Gemfile +++ b/Gemfile @@ -144,7 +144,7 @@ group :test do # Required for integration tests gem 'capybara', '~> 2.5.0' - gem 'poltergeist', '~> 1.7.0' + gem 'poltergeist', '~> 1.8.0' gem 'launchy', '~> 2.4.3' gem 'cucumber-rails', '~> 1.4', require: false From 4c5d52c5c4bb850a4d55f6f6ce41a01fab9ab633 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 16 Nov 2015 08:38:55 +0100 Subject: [PATCH 056/240] Bundle update. --- Gemfile.lock | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4d030ca9d..bd964f8fc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM sass (>= 3.3.0) bootstrap-select-rails (1.6.3) builder (3.0.4) - byebug (8.0.1) + byebug (8.2.0) cancan (1.6.10) capistrano (3.4.0) i18n @@ -151,7 +151,7 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) + coffee-script-source (1.10.0) colorize (0.7.7) compass (1.0.3) chunky_png (~> 1.2) @@ -353,8 +353,8 @@ GEM mini_portile (~> 0.6.0) options (2.3.2) orm_adapter (0.5.0) - pg (0.18.3) - poltergeist (1.7.0) + pg (0.18.4) + poltergeist (1.8.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -428,8 +428,8 @@ GEM rdf (~> 1.1, >= 1.1.6) rdf-rdfa (~> 1.1, >= 1.1.4.1) rdf-xsd (~> 1.1) - rdf-xsd (1.1.5) - rdf (~> 1.1, >= 1.1.9) + rdf-xsd (1.99.0) + rdf (~> 1.99) rdoc (3.12.2) json (~> 1.4) redcarpet (3.3.3) @@ -467,7 +467,6 @@ GEM rspec-core (~> 2.99.0) rspec-expectations (~> 2.99.0) rspec-mocks (~> 2.99.0) - rspec-support (3.3.0) ruby-graphviz (1.2.2) ruby-prof (0.15.8) rubydns (0.8.5) @@ -537,7 +536,7 @@ GEM tilt (1.4.1) timers (4.1.1) hitimes - tins (1.6.0) + tins (1.7.0) treetop (1.4.15) polyglot polyglot (>= 0.3.1) @@ -621,7 +620,7 @@ DEPENDENCIES nokogiri (~> 1.6.6.2) ontology-united! pg (~> 0.18.1) - poltergeist (~> 1.7.0) + poltergeist (~> 1.8.0) progress_bar (~> 1.0.2) pry-byebug (~> 3.3.0) pry-rails (~> 0.3.2) From 7de3dbd34f4bf4815e5ae52da9b3a9756d6c3835 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Wed, 18 Nov 2015 18:44:21 +0100 Subject: [PATCH 057/240] update ES installation in prod deployment wiki --- doc/productive_deployment.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/productive_deployment.md b/doc/productive_deployment.md index 17b15fd8f..c826c308e 100644 --- a/doc/productive_deployment.md +++ b/doc/productive_deployment.md @@ -403,18 +403,27 @@ Start the git daemon: If there's a new version on http://www.elasticsearch.org/download/, just replace the version number. + apt-get update - + apt-get install openjdk-7-jre-headless -y - wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.7.3.deb + + apt-get install openjdk-8-jre-headless -y + wget https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.0.0/elasticsearch-2.0.0.deb + +Before updating Elasticsearch to a new version it's recommended to get rid of the legacy version with `+ apt-get purge elasticsearch`. + +The new version (`2.0.0`) appears to be a bit buggy with the start/stop service, because there's no `/etc/default/elasticsearch` at a new installation so you need to `echo 'ES_USER=esearch\nES_GROUP=esearch' | \ + tee /etc/default/elasticsearch`. Before you can start Elasticsearch you need to replace the `elasticsearch` user with the `esearch` user. Thats because there are only 8-character-accounts on our machines allowed. To do this you need to - + dpkg --unpack elasticsearch-1.7.3.deb + + dpkg --unpack elasticsearch-2.0.0.deb + sed -r -e '/^#?ES_(USER|GROUP)=/ { s,^#,, ; s,=.*,=esearch, }' -i /etc/default/elasticsearch.dpkg-new + sed -e '/rmdir/ s,$, || true,' -i /var/lib/dpkg/info/elasticsearch.postrm + dpkg --configure elasticsearch + update-rc.d elasticsearch defaults 95 10 -Afterwards you can start it with +To have the permissions for `/etc/elasticsearch` with the `esearch` user you need to + + + find /etc/elasticsearch -type d -exec chmod 0755 {} + + + find /etc/elasticsearch -type f -exec chmod 0644 {} + + +Afterwards, if everything goes well, you can start it with + service elasticsearch start From 0b3ea4a7c8bbdb01bcdee92a1549dea7d9af32e0 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 28 Nov 2015 12:34:01 +0100 Subject: [PATCH 058/240] Remove spec for removed cleanup method. --- spec/models/sine_axiom_selection_spec.rb | 35 ------------------------ 1 file changed, 35 deletions(-) diff --git a/spec/models/sine_axiom_selection_spec.rb b/spec/models/sine_axiom_selection_spec.rb index 204493b97..4f99a335b 100644 --- a/spec/models/sine_axiom_selection_spec.rb +++ b/spec/models/sine_axiom_selection_spec.rb @@ -97,41 +97,6 @@ end end - context 'Methods' do - setup_hets - let(:ontology_fixture_file) { %w(prove/Subclass casl) } - let(:ontology_filepath) { ontology_fixture_file.join('.') } - before { stub_hets_for(ontology_filepath) } - - let(:sine_axiom_selection) do - create :sine_axiom_selection, - :with_auxiliary_objects, - ontology_fixture_file: ontology_fixture_file - end - subject { sine_axiom_selection } - - context 'cleanup' do - [SineSymbolCommonness, SineSymbolAxiomTrigger].each do |klass| - it "remove all #{klass} objects" do - subject.call - subject.send(:cleanup) - expect(klass.count).to eq(0) - end - end - end - - context 'destroy' do - before do - allow(subject).to receive(:cleanup) - end - - it 'calls cleanup' do - subject.destroy - expect(subject).to have_received(:cleanup) - end - end - end - context 'calling SInE' do setup_hets let(:ontology_fixture_file) { %w(prove/Subclass casl) } From 28aa386140910b0428b627bc72ddbe1e360dd936 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 25 Nov 2015 22:57:32 +0100 Subject: [PATCH 059/240] Add SPASS special SZS handling. --- lib/hets/prove/szs_parser.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/hets/prove/szs_parser.rb b/lib/hets/prove/szs_parser.rb index e1f609512..deecac8e5 100644 --- a/lib/hets/prove/szs_parser.rb +++ b/lib/hets/prove/szs_parser.rb @@ -34,6 +34,14 @@ def parse_status_eprover regex_parse_status(/\n# SZS status (\w+)/) end + def parse_status_spass + if match = generic_parse_status + match + elsif output.match(/^SPASS beiseite: Ran out of time.$/) + 'ResourceOut' + end + end + def regex_parse_status(regex) match = output.match(regex) match[1] if match From 69b24984ae4c3d77471c91e9b691c328a98b715e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 28 Nov 2015 12:28:35 +0100 Subject: [PATCH 060/240] Transform darwin TMO to RSO for uniformity. --- lib/hets/prove/szs_parser.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/hets/prove/szs_parser.rb b/lib/hets/prove/szs_parser.rb index deecac8e5..2e9ae84ca 100644 --- a/lib/hets/prove/szs_parser.rb +++ b/lib/hets/prove/szs_parser.rb @@ -23,7 +23,12 @@ def generic_parse_status end def parse_status_darwin - regex_parse_status(/\n\nSZS status (\w+) for/) + status = regex_parse_status(/\n\nSZS status (\w+)/) + if status == 'Timeout' + 'ResourceOut' + else + status + end end def parse_status_darwin_non_fd From 297de63a6fa495c316164e8e37892bfc5e96e2c6 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 28 Nov 2015 12:29:35 +0100 Subject: [PATCH 061/240] Adjust ProverOutputGenerator for RSO. --- spec/lib/hets/szs_parser_spec.rb | 12 +++++++++--- .../fixtures_generation/prover_output_generator.rb | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/spec/lib/hets/szs_parser_spec.rb b/spec/lib/hets/szs_parser_spec.rb index 32a891e90..d7323248b 100644 --- a/spec/lib/hets/szs_parser_spec.rb +++ b/spec/lib/hets/szs_parser_spec.rb @@ -1,13 +1,19 @@ require 'spec_helper' describe Hets::Prove::SZSParser do - %w(Theorem CounterSatisfiable).each do |szs_status| + %w(Theorem CounterSatisfiable ResourceOut).each do |szs_status| %w(SPASS).each do |prover| context "#{prover} on #{szs_status}" do let(:output) { File.read(prover_output_fixture(szs_status, prover)) } - it "returns nil" do - expect(Hets::Prove::SZSParser.new(prover, output).call).to be(nil) + if szs_status == 'ResourceOut' + it "returns ResourceOut" do + expect(Hets::Prove::SZSParser.new(prover, output).call).to eq('ResourceOut') + end + else + it "returns nil" do + expect(Hets::Prove::SZSParser.new(prover, output).call).to be(nil) + end end end end diff --git a/spec/support/fixtures_generation/prover_output_generator.rb b/spec/support/fixtures_generation/prover_output_generator.rb index 1feb3b739..b49f93ed6 100644 --- a/spec/support/fixtures_generation/prover_output_generator.rb +++ b/spec/support/fixtures_generation/prover_output_generator.rb @@ -2,7 +2,7 @@ module FixturesGeneration class ProverOutputGenerator < DirectHetsGenerator - NODES = %w(CounterSatisfiable Theorem) + NODES = %w(CounterSatisfiable Theorem ResourceOut) PROVERS = %w(SPASS darwin darwin-non-fd eprover) protected @@ -22,7 +22,7 @@ def perform(file) end def files - %w(spec/fixtures/ontologies/prove/Simple_Implications.casl) + %w(spec/fixtures/ontologies/prove/prover_output_generator.casl) end def subdir From 3e5dd87f67010dcea4642e1d32a6d8fe57daf677 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 28 Nov 2015 12:30:42 +0100 Subject: [PATCH 062/240] Add fixtures for RSO detection. --- .../prove/prover_output_generator.casl | 70 ++++ spec/fixtures/prover_output/ResourceOut/SPASS | 375 ++++++++++++++++++ .../fixtures/prover_output/ResourceOut/darwin | 38 ++ .../prover_output/ResourceOut/darwin-non-fd | 37 ++ .../prover_output/ResourceOut/eprover | 33 ++ 5 files changed, 553 insertions(+) create mode 100644 spec/fixtures/ontologies/prove/prover_output_generator.casl create mode 100644 spec/fixtures/prover_output/ResourceOut/SPASS create mode 100644 spec/fixtures/prover_output/ResourceOut/darwin create mode 100644 spec/fixtures/prover_output/ResourceOut/darwin-non-fd create mode 100644 spec/fixtures/prover_output/ResourceOut/eprover diff --git a/spec/fixtures/ontologies/prove/prover_output_generator.casl b/spec/fixtures/ontologies/prove/prover_output_generator.casl new file mode 100644 index 000000000..5ecf4f843 --- /dev/null +++ b/spec/fixtures/ontologies/prove/prover_output_generator.casl @@ -0,0 +1,70 @@ +spec Group = + sort s + ops 0:s; + -__ :s->s; + __+__ :s*s->s, assoc + forall x,y:s + . x+(-x) = 0 + . x+0=x %(leftunit)% + . 0+x=x %(rightunit)% %implied + . 0+0=0 %(zero_plus)% %implied +end + +spec Theorem = + sort s + ops 0:s + forall x,y:s . x = x %implied +end + +spec CounterSatisfiable = + sort s + ops 0:s; + 1:s + forall x:s . x = 0 + . not( 1 = 0 ) %implied +end + + + +spec Category = + sort Bool, Arrow + op True : Bool + + op __eEq__ : Arrow * Arrow -> Bool, comm + forall f, g : Arrow + . f eEq g = True => f eEq f = True + . f eEq g = True => f = g + + op __comp__ : Arrow * Arrow -> Arrow, assoc + forall f, g : Arrow + . (f comp g) eEq (f comp g) = True => f eEq f = True /\ g eEq g = True + + forall f, g, h : Arrow + . (f eEq f = True) /\ (g eEq g = True) /\ (h eEq h = True) => + ( (f comp g) eEq (f comp g) = True /\ + (g comp h) eEq (g comp h) = True => (f comp g comp h) eEq (f comp g comp h) = True ) +end + +spec ResourceOut = + Category then + + pred CommSquare : Arrow * Arrow * Arrow * Arrow + %[ * - f -> * ]% + %[ g g' ]% + %[ * - f'-> * ]% + forall f, g, f', g' : Arrow + . (f eEq f = True) /\ (g eEq g = True) /\ (f' eEq f' = True) /\ (g' eEq g' = True) => + ( CommSquare(f, g, f', g') <=> (f comp g') eEq (g comp f') = True ) %(CommSquareAx)% + +then %implies + %[Horizontal glueing of commutative squares]% + + %[ * -f -> * -h -> * ]% + %[ g g' g'' ]% + %[ * -f'-> * -h'-> * ]% + + forall f, g, f', g', h, h', g'' : Arrow + . (f eEq f = True) /\ (g eEq g = True) /\ (f' eEq f' = True) /\ (g' eEq g' = True) /\ (h eEq h = True) /\ (h' eEq h' = True) /\ (g'' eEq g'' = True) => + ( CommSquare(f, g, f', g') /\ CommSquare(h, g', h', g'') => + CommSquare(f comp h, g, f' comp h', g'') ) %(CommSquare-horizontal-glueing)% %implied +end diff --git a/spec/fixtures/prover_output/ResourceOut/SPASS b/spec/fixtures/prover_output/ResourceOut/SPASS new file mode 100644 index 000000000..5cbf8913d --- /dev/null +++ b/spec/fixtures/prover_output/ResourceOut/SPASS @@ -0,0 +1,375 @@ + +--------------------------SPASS-START----------------------------- +Input Problem: +1[0:Inp] || -> arrow(skc15)*. +2[0:Inp] || -> arrow(skc14)*. +3[0:Inp] || -> arrow(skc13)*. +4[0:Inp] || -> arrow(skc12)*. +5[0:Inp] || -> arrow(skc11)*. +6[0:Inp] || -> arrow(skc10)*. +7[0:Inp] || -> arrow(skc9)*. +8[0:Inp] || -> arrow(skc16)*. +9[0:Inp] || -> bool(skc17)*. +10[0:Inp] || -> bool(x_True)*. +11[0:Inp] || -> equal(o__eEq__(skc15,skc15),x_True)**. +12[0:Inp] || -> commSquare(skc15,skc14,skc13,skc12)*. +13[0:Inp] || -> equal(o__eEq__(skc14,skc14),x_True)**. +14[0:Inp] || -> equal(o__eEq__(skc13,skc13),x_True)**. +15[0:Inp] || -> commSquare(skc11,skc12,skc10,skc9)*. +16[0:Inp] || -> equal(o__eEq__(skc12,skc12),x_True)**. +17[0:Inp] || -> equal(o__eEq__(skc11,skc11),x_True)**. +18[0:Inp] || -> equal(o__eEq__(skc10,skc10),x_True)**. +19[0:Inp] || -> equal(o__eEq__(skc9,skc9),x_True)**. +20[0:Inp] || arrow(U) bool(U)* -> . +21[0:Inp] || commSquare(U,V,W,X)* -> arrow(U). +22[0:Inp] || commSquare(U,V,W,X)* -> arrow(V). +23[0:Inp] || commSquare(U,V,W,X)* -> arrow(W). +24[0:Inp] || commSquare(U,V,W,X)* -> arrow(X). +25[0:Inp] || commSquare(o__comp__(skc15,skc11),skc14,o__comp__(skc13,skc10),skc9)* -> . +26[0:Inp] || arrow(U) arrow(V) -> arrow(o__comp__(U,V))*. +27[0:Inp] || arrow(U) arrow(V) -> bool(o__eEq__(U,V))*. +28[0:Inp] || arrow(U) arrow(V) -> equal(o__eEq__(U,V),o__eEq__(V,U))*. +29[0:Inp] || arrow(U) equal(o__eEq__(U,V),x_True)** arrow(V) -> equal(U,V). +30[0:Inp] || arrow(U) equal(o__eEq__(U,V),x_True)** arrow(V) -> equal(o__eEq__(U,U),x_True)**. +31[0:Inp] || arrow(U) arrow(V) arrow(W) -> equal(o__comp__(o__comp__(U,V),W),o__comp__(U,o__comp__(V,W)))**. +32[0:Inp] || arrow(U) equal(o__eEq__(o__comp__(U,V),o__comp__(U,V)),x_True)** arrow(V) -> equal(o__eEq__(V,V),x_True). +33[0:Inp] || arrow(U) equal(o__eEq__(o__comp__(U,V),o__comp__(U,V)),x_True)** arrow(V) -> equal(o__eEq__(U,U),x_True). +34[0:Inp] || equal(o__eEq__(o__comp__(U,V),o__comp__(W,X)),x_True)** arrow(U) equal(o__eEq__(U,U),x_True)** equal(o__eEq__(W,W),x_True)** arrow(W) arrow(X) equal(o__eEq__(X,X),x_True)** equal(o__eEq__(V,V),x_True)** arrow(V) -> commSquare(U,W,X,V). +35[0:Inp] || commSquare(U,V,W,X) arrow(U) equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** arrow(V) arrow(W) equal(o__eEq__(W,W),x_True)** equal(o__eEq__(X,X),x_True)** arrow(X) -> equal(o__eEq__(o__comp__(U,X),o__comp__(V,W)),x_True)**. +36[0:Inp] || arrow(U) equal(o__eEq__(U,U),x_True) equal(o__eEq__(o__comp__(U,V),o__comp__(U,V)),x_True) equal(o__eEq__(o__comp__(V,W),o__comp__(V,W)),x_True) equal(o__eEq__(V,V),x_True) arrow(V) arrow(W) equal(o__eEq__(W,W),x_True) -> equal(o__eEq__(o__comp__(o__comp__(U,V),W),o__comp__(o__comp__(U,V),W)),x_True)**. + This is a first-order Horn problem containing equality. + This is a problem that contains sort information. + All equations are many sorted. + The conjecture is ground. + Axiom clauses: 19 Conjecture clauses: 17 + Inferences: IEmS=1 ISoR=1 IEqR=1 ISpR=1 ISpL=1 IORe=1 + Reductions: RFRew=1 RBRew=1 RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RSST=1 RSSi=1 RFSub=1 RBSub=1 RAED=1 RCon=1 + Extras : Input Saturation, Dynamic Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1 + Precedence: o__eEq__ > o__comp__ > bool > arrow > commSquare > skc17 > skc16 > skc15 > skc14 > skc13 > skc12 > skc11 > skc10 > skc9 > skc8 > skc7 > skc6 > skc5 > skc4 > skc3 > skc2 > skc1 > skc0 > x_True + Ordering : KBO +Processed Problem: + +Worked Off Clauses: + +Usable Clauses: +10[0:Inp] || -> bool(x_True)*. +9[0:Inp] || -> bool(skc17)*. +8[0:Inp] || -> arrow(skc16)*. +7[0:Inp] || -> arrow(skc9)*. +6[0:Inp] || -> arrow(skc10)*. +5[0:Inp] || -> arrow(skc11)*. +4[0:Inp] || -> arrow(skc12)*. +3[0:Inp] || -> arrow(skc13)*. +2[0:Inp] || -> arrow(skc14)*. +1[0:Inp] || -> arrow(skc15)*. +50[0:Res:7.0,20.0] bool(skc9) || -> . +75[0:Res:6.0,20.0] bool(skc10) || -> . +15[0:Inp] || -> commSquare(skc11,skc12,skc10,skc9)*. +12[0:Inp] || -> commSquare(skc15,skc14,skc13,skc12)*. +19[0:Inp] || -> equal(o__eEq__(skc9,skc9),x_True)**. +18[0:Inp] || -> equal(o__eEq__(skc10,skc10),x_True)**. +17[0:Inp] || -> equal(o__eEq__(skc11,skc11),x_True)**. +16[0:Inp] || -> equal(o__eEq__(skc12,skc12),x_True)**. +14[0:Inp] || -> equal(o__eEq__(skc13,skc13),x_True)**. +13[0:Inp] || -> equal(o__eEq__(skc14,skc14),x_True)**. +11[0:Inp] || -> equal(o__eEq__(skc15,skc15),x_True)**. +20[0:Inp] bool(U) arrow(U) || -> . +49[0:Res:7.0,27.0] arrow(U) || -> bool(o__eEq__(skc9,U))*. +74[0:Res:6.0,27.0] arrow(U) || -> bool(o__eEq__(skc10,U))*. +48[0:Res:7.0,26.0] arrow(U) || -> arrow(o__comp__(skc9,U))*. +73[0:Res:6.0,26.0] arrow(U) || -> arrow(o__comp__(skc10,U))*. +64[0:Res:7.0,27.1] arrow(U) || -> bool(o__eEq__(U,skc9))*. +89[0:Res:6.0,27.1] arrow(U) || -> bool(o__eEq__(U,skc10))*. +63[0:Res:7.0,26.1] arrow(U) || -> arrow(o__comp__(U,skc9))*. +88[0:Res:6.0,26.1] arrow(U) || -> arrow(o__comp__(U,skc10))*. +24[0:Inp] || commSquare(U,V,W,X)* -> arrow(X). +23[0:Inp] || commSquare(U,V,W,X)* -> arrow(W). +22[0:Inp] || commSquare(U,V,W,X)* -> arrow(V). +21[0:Inp] || commSquare(U,V,W,X)* -> arrow(U). +25[0:Inp] || commSquare(o__comp__(skc15,skc11),skc14,o__comp__(skc13,skc10),skc9)* -> . +27[0:Inp] arrow(U) arrow(V) || -> bool(o__eEq__(V,U))*. +26[0:Inp] arrow(U) arrow(V) || -> arrow(o__comp__(V,U))*. +47[0:Res:7.0,28.0] arrow(U) || -> equal(o__eEq__(skc9,U),o__eEq__(U,skc9))*. +72[0:Res:6.0,28.0] arrow(U) || -> equal(o__eEq__(skc10,U),o__eEq__(U,skc10))*. +46[0:Res:7.0,29.0] arrow(U) || equal(o__eEq__(skc9,U),x_True)** -> equal(skc9,U). +71[0:Res:6.0,29.0] arrow(U) || equal(o__eEq__(skc10,U),x_True)** -> equal(skc10,U). +61[0:Res:7.0,29.1] arrow(U) || equal(o__eEq__(U,skc9),x_True)** -> equal(U,skc9). +86[0:Res:6.0,29.1] arrow(U) || equal(o__eEq__(U,skc10),x_True)** -> equal(U,skc10). +28[0:Inp] arrow(U) arrow(V) || -> equal(o__eEq__(V,U),o__eEq__(U,V))*. +60[0:Res:7.0,30.1] arrow(U) || equal(o__eEq__(U,skc9),x_True)** -> equal(o__eEq__(U,U),x_True)**. +85[0:Res:6.0,30.1] arrow(U) || equal(o__eEq__(U,skc10),x_True)** -> equal(o__eEq__(U,U),x_True)**. +29[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(V,U),x_True)** -> equal(V,U). +30[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(V,U),x_True)** -> equal(o__eEq__(V,V),x_True)**. +44[0:Res:7.0,31.0] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(skc9,V),U),o__comp__(skc9,o__comp__(V,U)))**. +69[0:Res:6.0,31.0] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(skc10,V),U),o__comp__(skc10,o__comp__(V,U)))**. +59[0:Res:7.0,31.1] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,skc9),U),o__comp__(V,o__comp__(skc9,U)))**. +84[0:Res:6.0,31.1] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,skc10),U),o__comp__(V,o__comp__(skc10,U)))**. +54[0:Res:7.0,31.2] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,U),skc9),o__comp__(V,o__comp__(U,skc9)))**. +79[0:Res:6.0,31.2] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,U),skc10),o__comp__(V,o__comp__(U,skc10)))**. +43[0:Res:7.0,32.0] arrow(U) || equal(o__eEq__(o__comp__(skc9,U),o__comp__(skc9,U)),x_True)** -> equal(o__eEq__(U,U),x_True). +68[0:Res:6.0,32.0] arrow(U) || equal(o__eEq__(o__comp__(skc10,U),o__comp__(skc10,U)),x_True)** -> equal(o__eEq__(U,U),x_True). +57[0:Res:7.0,33.1] arrow(U) || equal(o__eEq__(o__comp__(U,skc9),o__comp__(U,skc9)),x_True)** -> equal(o__eEq__(U,U),x_True). +82[0:Res:6.0,33.1] arrow(U) || equal(o__eEq__(o__comp__(U,skc10),o__comp__(U,skc10)),x_True)** -> equal(o__eEq__(U,U),x_True). +31[0:Inp] arrow(U) arrow(V) arrow(W) || -> equal(o__comp__(o__comp__(W,V),U),o__comp__(W,o__comp__(V,U)))**. +32[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True)** -> equal(o__eEq__(U,U),x_True). +33[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True)** -> equal(o__eEq__(V,V),x_True). +37[0:MRR:35.0,35.1,35.2,35.3,21.1,22.1,23.1,24.1] || equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(X,X),x_True)** commSquare(X,U,W,V) -> equal(o__eEq__(o__comp__(X,V),o__comp__(U,W)),x_True)**. +65[0:Res:6.0,39.0] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(skc10,V),o__comp__(skc10,V)),x_True) -> equal(o__eEq__(o__comp__(skc10,o__comp__(V,U)),o__comp__(skc10,o__comp__(V,U))),x_True)**. +40[0:Res:7.0,39.0] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(skc9,V),o__comp__(skc9,V)),x_True) -> equal(o__eEq__(o__comp__(skc9,o__comp__(V,U)),o__comp__(skc9,o__comp__(V,U))),x_True)**. +80[0:Res:6.0,39.1] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,skc10),o__comp__(V,skc10)),x_True) equal(o__eEq__(o__comp__(skc10,U),o__comp__(skc10,U)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(skc10,U)),o__comp__(V,o__comp__(skc10,U))),x_True)**. +55[0:Res:7.0,39.1] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,skc9),o__comp__(V,skc9)),x_True) equal(o__eEq__(o__comp__(skc9,U),o__comp__(skc9,U)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(skc9,U)),o__comp__(V,o__comp__(skc9,U))),x_True)**. +77[0:Res:6.0,39.2] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(U,skc10),o__comp__(U,skc10)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(U,skc10)),o__comp__(V,o__comp__(U,skc10))),x_True)**. +52[0:Res:7.0,39.2] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(U,skc9),o__comp__(U,skc9)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(U,skc9)),o__comp__(V,o__comp__(U,skc9))),x_True)**. +39[0:Obv:38.7] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(o__comp__(W,V),o__comp__(W,V)),x_True) equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) -> equal(o__eEq__(o__comp__(W,o__comp__(V,U)),o__comp__(W,o__comp__(V,U))),x_True)**. +97[0:Obv:96.4] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(o__comp__(skc10,U),o__comp__(W,V)),x_True)** -> commSquare(skc10,W,V,U). +105[0:Obv:104.4] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(o__comp__(skc9,U),o__comp__(W,V)),x_True)** -> commSquare(skc9,W,V,U). +91[0:Obv:90.7] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,U),o__comp__(skc10,V)),x_True)** -> commSquare(W,skc10,V,U). +99[0:Obv:98.7] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,U),o__comp__(skc9,V)),x_True)** -> commSquare(W,skc9,V,U). +93[0:Obv:92.5] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(V,V),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,U),o__comp__(V,skc10)),x_True)** -> commSquare(W,V,skc10,U). +101[0:Obv:100.5] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(V,V),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,U),o__comp__(V,skc9)),x_True)** -> commSquare(W,V,skc9,U). +95[0:Obv:94.6] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(V,V),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,skc10),o__comp__(V,U)),x_True)** -> commSquare(W,V,U,skc10). +103[0:Obv:102.6] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(V,V),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,skc9),o__comp__(V,U)),x_True)** -> commSquare(W,V,U,skc9). +34[0:Inp] arrow(U) arrow(V) arrow(W) arrow(X) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(X,X),x_True)** equal(o__eEq__(o__comp__(X,U),o__comp__(W,V)),x_True)** -> commSquare(X,W,V,U). + Given clause: 10[0:Inp] || -> bool(x_True)*. + Given clause: 9[0:Inp] || -> bool(skc17)*. + Given clause: 8[0:Inp] || -> arrow(skc16)*. + Given clause: 7[0:Inp] || -> arrow(skc9)*. + Given clause: 6[0:Inp] || -> arrow(skc10)*. + Given clause: 5[0:Inp] || -> arrow(skc11)*. + Given clause: 4[0:Inp] || -> arrow(skc12)*. + Given clause: 3[0:Inp] || -> arrow(skc13)*. + Given clause: 2[0:Inp] || -> arrow(skc14)*. + Given clause: 1[0:Inp] || -> arrow(skc15)*. + Given clause: 50[0:Res:7.0,20.0] bool(skc9) || -> . + Given clause: 75[0:Res:6.0,20.0] bool(skc10) || -> . + Given clause: 15[0:Inp] || -> commSquare(skc11,skc12,skc10,skc9)*. + Given clause: 12[0:Inp] || -> commSquare(skc15,skc14,skc13,skc12)*. + Given clause: 19[0:Inp] || -> equal(o__eEq__(skc9,skc9),x_True)**. + Given clause: 18[0:Inp] || -> equal(o__eEq__(skc10,skc10),x_True)**. + Given clause: 17[0:Inp] || -> equal(o__eEq__(skc11,skc11),x_True)**. + Given clause: 16[0:Inp] || -> equal(o__eEq__(skc12,skc12),x_True)**. + Given clause: 14[0:Inp] || -> equal(o__eEq__(skc13,skc13),x_True)**. + Given clause: 13[0:Inp] || -> equal(o__eEq__(skc14,skc14),x_True)**. + Given clause: 11[0:Inp] || -> equal(o__eEq__(skc15,skc15),x_True)**. + Given clause: 20[0:Inp] bool(U) arrow(U) || -> . + Given clause: 49[0:Res:7.0,27.0] arrow(U) || -> bool(o__eEq__(skc9,U))*. + Given clause: 74[0:Res:6.0,27.0] arrow(U) || -> bool(o__eEq__(skc10,U))*. + Given clause: 24[0:Inp] || commSquare(U,V,W,X)* -> arrow(X). + Given clause: 48[0:Res:7.0,26.0] arrow(U) || -> arrow(o__comp__(skc9,U))*. + Given clause: 73[0:Res:6.0,26.0] arrow(U) || -> arrow(o__comp__(skc10,U))*. + Given clause: 64[0:Res:7.0,27.1] arrow(U) || -> bool(o__eEq__(U,skc9))*. + Given clause: 89[0:Res:6.0,27.1] arrow(U) || -> bool(o__eEq__(U,skc10))*. + Given clause: 23[0:Inp] || commSquare(U,V,W,X)* -> arrow(W). + Given clause: 63[0:Res:7.0,26.1] arrow(U) || -> arrow(o__comp__(U,skc9))*. + Given clause: 88[0:Res:6.0,26.1] arrow(U) || -> arrow(o__comp__(U,skc10))*. + Given clause: 22[0:Inp] || commSquare(U,V,W,X)* -> arrow(V). + Given clause: 21[0:Inp] || commSquare(U,V,W,X)* -> arrow(U). + Given clause: 25[0:Inp] || commSquare(o__comp__(skc15,skc11),skc14,o__comp__(skc13,skc10),skc9)* -> . + Given clause: 47[0:Res:7.0,28.0] arrow(U) || -> equal(o__eEq__(skc9,U),o__eEq__(U,skc9))*. + Given clause: 72[0:Res:6.0,28.0] arrow(U) || -> equal(o__eEq__(skc10,U),o__eEq__(U,skc10))*. + Given clause: 27[0:Inp] arrow(U) arrow(V) || -> bool(o__eEq__(V,U))*. + Given clause: 26[0:Inp] arrow(U) arrow(V) || -> arrow(o__comp__(V,U))*. + Given clause: 28[0:Inp] arrow(U) arrow(V) || -> equal(o__eEq__(V,U),o__eEq__(U,V))*. + Given clause: 46[0:Res:7.0,29.0] arrow(U) || equal(o__eEq__(skc9,U),x_True)** -> equal(skc9,U). + Given clause: 71[0:Res:6.0,29.0] arrow(U) || equal(o__eEq__(skc10,U),x_True)** -> equal(skc10,U). + Given clause: 61[0:Res:7.0,29.1] arrow(U) || equal(o__eEq__(U,skc9),x_True)** -> equal(U,skc9). + Given clause: 86[0:Res:6.0,29.1] arrow(U) || equal(o__eEq__(U,skc10),x_True)** -> equal(U,skc10). + Given clause: 29[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(V,U),x_True)** -> equal(V,U). + Given clause: 60[0:Res:7.0,30.1] arrow(U) || equal(o__eEq__(U,skc9),x_True)**+ -> equal(o__eEq__(U,U),x_True)**. + Given clause: 85[0:Res:6.0,30.1] arrow(U) || equal(o__eEq__(U,skc10),x_True)**+ -> equal(o__eEq__(U,U),x_True)**. + Given clause: 322[0:Obv:318.0] arrow(U) || equal(o__eEq__(skc9,U),x_True)**+ -> equal(o__eEq__(U,U),x_True)**. + Given clause: 331[0:Obv:327.0] arrow(U) || equal(o__eEq__(skc10,U),x_True)**+ -> equal(o__eEq__(U,U),x_True)**. + Given clause: 30[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(V,U),x_True)**+ -> equal(o__eEq__(V,V),x_True)**. + Given clause: 44[0:Res:7.0,31.0] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(skc9,V),U),o__comp__(skc9,o__comp__(V,U)))**. + Given clause: 383[0:SSi:381.2,381.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(U,skc9)))*. + Given clause: 384[0:SSi:382.2,382.0,48.0,6.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(U,skc10)))*. + Given clause: 388[0:SSi:387.2,387.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9))))*. + Given clause: 31[0:Inp] arrow(U) arrow(V) arrow(W) || -> equal(o__comp__(o__comp__(W,V),U),o__comp__(W,o__comp__(V,U)))**. + Given clause: 390[0:SSi:389.2,389.0,48.0,6.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc10))))*. + Given clause: 386[0:SSi:385.2,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(U,V)))*. + Given clause: 403[0:SSi:399.3,399.0,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(U,skc10)))*. + Given clause: 404[0:SSi:396.3,396.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(U,skc9)))*. + Given clause: 32[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True)** -> equal(o__eEq__(U,U),x_True). + Given clause: 392[0:SSi:391.2,391.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 416[0:SSi:414.2,414.0,48.0,6.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc10)))))*. + Given clause: 405[0:SSi:400.3,400.0,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(U,skc10))))*. + Given clause: 406[0:SSi:397.3,397.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(U,skc9))))*. + Given clause: 33[0:Inp] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True)** -> equal(o__eEq__(V,V),x_True). + Given clause: 421[0:SSi:420.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,V))))*. + Given clause: 428[0:SSi:424.2,424.0,48.0,6.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(U,skc10))))*. + Given clause: 434[0:SSi:430.2,430.0,48.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(U,skc9))))*. + Given clause: 452[0:SSi:450.2,450.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9))))))*. + Given clause: 37[0:MRR:35.0,35.1,35.2,35.3,21.1,22.1,23.1,24.1] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(X,X),x_True)** commSquare(X,U,W,V) -> equal(o__eEq__(o__comp__(X,V),o__comp__(U,W)),x_True)**. + Given clause: 456[0:SSi:454.2,454.0,48.0,6.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc10))))))*. + Given clause: 408[0:SSi:407.3,26.2] arrow(U) arrow(V) arrow(W) || -> arrow(o__comp__(V,o__comp__(U,W)))*. + Given clause: 409[0:SSi:398.3,398.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(V,o__comp__(U,skc9)))))*. + Given clause: 417[0:SSi:415.3,415.0,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(V,o__comp__(U,skc10)))))*. + Given clause: 39[0:Obv:38.7] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(o__comp__(W,V),o__comp__(W,V)),x_True) equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) -> equal(o__eEq__(o__comp__(W,o__comp__(V,U)),o__comp__(W,o__comp__(V,U))),x_True)**. + Given clause: 462[0:SSi:458.2,458.0,48.0,6.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(skc9,o__comp__(U,skc10)))))*. + Given clause: 468[0:SSi:464.2,464.0,48.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 487[0:SSi:486.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,V)))))*. + Given clause: 494[0:SSi:490.2,490.0,48.0,6.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc10)))))*. + Given clause: 34[0:Inp] arrow(U) arrow(V) arrow(W) arrow(X) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(X,X),x_True)** equal(o__eEq__(o__comp__(X,U),o__comp__(W,V)),x_True)**+ -> commSquare(X,W,V,U). + Given clause: 500[0:SSi:496.2,496.0,48.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 504[0:SSi:502.2,502.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9)))))))*. + Given clause: 530[0:SSi:528.2,528.0,48.0,6.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc10)))))))*. + Given clause: 69[0:Res:6.0,31.0] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(skc10,V),U),o__comp__(skc10,o__comp__(V,U)))**. + Given clause: 59[0:Res:7.0,31.1] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,skc9),U),o__comp__(V,o__comp__(skc9,U)))**. + Given clause: 700[0:SSi:653.2,653.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,skc9)))*. + Given clause: 794[0:SSi:755.2,755.0,26.0,7.0,6.2] arrow(U) || -> arrow(o__comp__(U,o__comp__(skc9,skc10)))*. + Given clause: 701[0:SSi:654.2,654.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9))))*. + Given clause: 795[0:SSi:756.2,756.0,26.0,7.0,6.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc10))))*. + Given clause: 84[0:Res:6.0,31.1] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,skc10),U),o__comp__(V,o__comp__(skc10,U)))**. + Given clause: 919[0:SSi:862.2,862.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(U,o__comp__(skc10,skc9)))*. + Given clause: 836[0:SSi:832.2,832.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,skc9))))*. + Given clause: 837[0:SSi:834.2,834.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9))))*. + Given clause: 838[0:SSi:835.2,835.0,26.0,7.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9))))*. + Given clause: 54[0:Res:7.0,31.2] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,U),skc9),o__comp__(V,o__comp__(U,skc9)))**. + Given clause: 920[0:SSi:863.2,863.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9))))*. + Given clause: 921[0:SSi:874.2,874.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9))))*. + Given clause: 703[0:SSi:702.2,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(U,V)))*. + Given clause: 797[0:SSi:796.2,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(U,o__comp__(skc9,V)))*. + Given clause: 79[0:Res:6.0,31.2] arrow(U) arrow(V) || -> equal(o__comp__(o__comp__(V,U),skc10),o__comp__(V,o__comp__(U,skc10)))**. + Given clause: 923[0:SSi:922.2,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(U,o__comp__(skc10,V)))*. + Given clause: 704[0:SSi:655.2,655.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 798[0:SSi:757.2,757.0,26.0,7.0,6.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc10)))))*. + Given clause: 848[0:SSi:847.2,847.0,26.0,7.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9)))))*. + Given clause: 377[0:Obv:365.1] arrow(U) arrow(V) || equal(o__eEq__(U,V),x_True)**+ -> equal(o__eEq__(V,V),x_True)**. + Given clause: 849[0:SSi:846.2,846.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 850[0:SSi:844.2,844.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 924[0:SSi:875.2,875.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 925[0:SSi:864.2,864.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 43[0:Res:7.0,32.0] arrow(U) || equal(o__eEq__(o__comp__(skc9,U),o__comp__(skc9,U)),x_True)** -> equal(o__eEq__(U,U),x_True). + Given clause: 970[0:SSi:969.2,969.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 971[0:SSi:968.2,968.0,26.0,7.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc9)))))*. + Given clause: 972[0:SSi:967.2,967.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 973[0:SSi:965.2,965.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 68[0:Res:6.0,32.0] arrow(U) || equal(o__eEq__(o__comp__(skc10,U),o__comp__(skc10,U)),x_True)** -> equal(o__eEq__(U,U),x_True). + Given clause: 980[0:SSi:979.2,979.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 981[0:SSi:978.2,978.0,26.0,7.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9)))))*. + Given clause: 982[0:SSi:977.2,977.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 983[0:SSi:975.2,975.0,48.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc9,o__comp__(U,skc9)))))*. + Given clause: 57[0:Res:7.0,33.1] arrow(U) || equal(o__eEq__(o__comp__(U,skc9),o__comp__(U,skc9)),x_True)** -> equal(o__eEq__(U,U),x_True). + Given clause: 990[0:SSi:989.2,989.0,26.0,6.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc9,skc9)))))*. + Given clause: 991[0:SSi:988.2,988.0,26.0,7.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc9,skc9)))))*. + Given clause: 1055[0:SSi:1054.2,1054.0,26.0,6.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc10,skc9)))))*. + Given clause: 1056[0:SSi:1053.2,1053.0,26.0,7.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc10,skc9)))))*. + Given clause: 82[0:Res:6.0,33.1] arrow(U) || equal(o__eEq__(o__comp__(U,skc10),o__comp__(U,skc10)),x_True)** -> equal(o__eEq__(U,U),x_True). + Given clause: 706[0:SSi:705.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,V))))*. + Given clause: 707[0:SSi:662.2,662.0,73.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc10,o__comp__(U,skc9))))*. + Given clause: 800[0:SSi:799.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(U,o__comp__(skc9,V))))*. + Given clause: 801[0:SSi:764.2,764.0,26.0,7.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(U,o__comp__(skc9,skc10))))*. + Given clause: 413[0:SSi:412.4,26.2] arrow(U) arrow(V) arrow(W) arrow(X) || -> equal(o__comp__(o__comp__(V,o__comp__(U,X)),W),o__comp__(o__comp__(V,U),o__comp__(X,W)))**. + Given clause: 839[0:SSi:833.3,833.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(V,o__comp__(U,skc9))))*. + Given clause: 927[0:SSi:926.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(U,o__comp__(skc10,V))))*. + Given clause: 928[0:SSi:871.2,871.0,26.0,6.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(U,o__comp__(skc10,skc9))))*. + Given clause: 1065[0:SSi:1064.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc10,V))))*. + Given clause: 1623[0:Rew:1622.4,413.4] arrow(U) arrow(V) arrow(W) arrow(X) || -> equal(o__comp__(o__comp__(V,o__comp__(U,X)),W),o__comp__(V,o__comp__(U,o__comp__(X,W))))**. + Given clause: 1067[0:SSi:1066.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(U,o__comp__(skc9,V))))*. + Given clause: 1069[0:SSi:1068.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,V))))*. + Given clause: 1071[0:SSi:1070.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,V))))*. + Given clause: 708[0:SSi:656.2,656.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 448[0:SSi:447.3,26.2] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(o__comp__(V,o__comp__(U,W)),o__comp__(V,o__comp__(U,W))),x_True)** -> equal(o__eEq__(W,W),x_True). + Given clause: 802[0:SSi:758.2,758.0,26.0,7.0,6.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc10))))))*. + Given clause: 929[0:SSi:865.2,865.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1143[0:SSi:1140.2,1140.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1144[0:SSi:1138.2,1138.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 482[0:SSi:481.3,26.2] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(o__comp__(V,o__comp__(U,W)),o__comp__(V,o__comp__(U,W))),x_True)** -> equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True). + Given clause: 1155[0:SSi:1154.2,1154.0,26.0,6.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc9,skc9))))))*. + Given clause: 1156[0:SSi:1153.2,1153.0,26.0,7.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc9,skc9))))))*. + Given clause: 1157[0:SSi:1152.2,1152.0,73.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 1158[0:SSi:1150.2,1150.0,48.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 519[0:Obv:514.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc15,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc15,V)),x_True)**. + Given clause: 1195[0:SSi:1192.2,1192.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1196[0:SSi:1190.2,1190.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 1204[0:SSi:1201.2,1201.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1205[0:SSi:1199.2,1199.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 520[0:Obv:513.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc14,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc14,V)),x_True)**. + Given clause: 1211[0:SSi:1210.2,1210.0,26.0,6.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc10,skc9))))))*. + Given clause: 1212[0:SSi:1209.2,1209.0,26.0,7.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc10,skc9))))))*. + Given clause: 1229[0:SSi:1228.2,1228.0,26.0,6.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,o__comp__(skc10,skc9))))))*. + Given clause: 1230[0:SSi:1227.2,1227.0,26.0,7.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,o__comp__(skc10,skc9))))))*. + Given clause: 521[0:Obv:512.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc13,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc13,V)),x_True)**. + Given clause: 1231[0:SSi:1226.2,1226.0,73.0,73.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1232[0:SSi:1224.2,1224.0,48.0,73.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1239[0:SSi:1238.2,1238.0,26.0,6.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,o__comp__(skc9,skc9))))))*. + Given clause: 1240[0:SSi:1237.2,1237.0,26.0,7.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,o__comp__(skc9,skc9))))))*. + Given clause: 522[0:Obv:511.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc12,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc12,V)),x_True)**. + Given clause: 1241[0:SSi:1236.2,1236.0,73.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 1242[0:SSi:1234.2,1234.0,48.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 1251[0:SSi:1246.2,1246.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 1259[0:SSi:1254.2,1254.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 523[0:Obv:510.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc11,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc11,V)),x_True)**. + Given clause: 1270[0:SSi:1269.2,1269.0,26.0,6.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc10,skc9))))))*. + Given clause: 1271[0:SSi:1268.2,1268.0,26.0,7.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc10,skc9))))))*. + Given clause: 1272[0:SSi:1267.2,1267.0,73.0,73.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 1273[0:SSi:1265.2,1265.0,48.0,73.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9))))))*. + Given clause: 524[0:Obv:508.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc10,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc10,V)),x_True)**. + Given clause: 1280[0:SSi:1279.2,1279.0,26.0,6.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc9,skc9))))))*. + Given clause: 1281[0:SSi:1278.2,1278.0,26.0,7.1,48.0,7.2] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc9,skc9))))))*. + Given clause: 1282[0:SSi:1277.2,1277.0,73.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 1283[0:SSi:1275.2,1275.0,48.0,48.1,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc9))))))*. + Given clause: 525[0:Obv:506.0] || equal(o__eEq__(U,U),x_True)**+ equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** commSquare(W,skc9,V,U) -> equal(o__eEq__(o__comp__(W,U),o__comp__(skc9,V)),x_True)**. + Given clause: 1292[0:SSi:1287.2,1287.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 1300[0:SSi:1295.2,1295.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9))))))*. + Given clause: 710[0:SSi:709.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,V)))))*. + Given clause: 711[0:SSi:663.2,663.0,73.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 634[0:SSi:633.2,26.2] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(o__comp__(W,V),o__comp__(W,V)),x_True) equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) -> commSquare(W,W,o__comp__(V,U),o__comp__(V,U))*. + Given clause: 712[0:SSi:660.2,660.0,73.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 804[0:SSi:803.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc9,V)))))*. + Given clause: 805[0:SSi:765.2,765.0,26.0,7.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(U,o__comp__(skc9,skc10)))))*. + Given clause: 806[0:SSi:762.2,762.0,26.0,7.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc10)))))*. + Given clause: 80[0:Res:6.0,39.1] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,skc10),o__comp__(V,skc10)),x_True) equal(o__eEq__(o__comp__(skc10,U),o__comp__(skc10,U)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(skc10,U)),o__comp__(V,o__comp__(skc10,U))),x_True)**. + Given clause: 851[0:SSi:845.3,845.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(V,o__comp__(U,skc9)))))*. + Given clause: 931[0:SSi:930.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc10,V)))))*. + Given clause: 932[0:SSi:872.2,872.0,26.0,6.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(V,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 933[0:SSi:869.2,869.0,26.0,6.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 55[0:Res:7.0,39.1] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,skc9),o__comp__(V,skc9)),x_True) equal(o__eEq__(o__comp__(skc9,U),o__comp__(skc9,U)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(skc9,U)),o__comp__(V,o__comp__(skc9,U))),x_True)**. + Given clause: 974[0:SSi:966.3,966.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(V,o__comp__(U,skc9)))))*. + Given clause: 984[0:SSi:976.3,976.0,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(V,o__comp__(U,skc9)))))*. + Given clause: 992[0:SSi:986.3,986.0,26.0,48.1,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(V,o__comp__(U,o__comp__(skc9,skc9)))))*. + Given clause: 1057[0:SSi:1051.3,1051.0,26.0,73.1,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(V,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 65[0:Res:6.0,39.0] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(skc10,V),o__comp__(skc10,V)),x_True) -> equal(o__eEq__(o__comp__(skc10,o__comp__(V,U)),o__comp__(skc10,o__comp__(V,U))),x_True)**. + Given clause: 1363[0:SSi:1362.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,V)))))*. + Given clause: 1365[0:SSi:1364.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,V)))))*. + Given clause: 1367[0:SSi:1366.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,V)))))*. + Given clause: 1369[0:SSi:1368.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc9,o__comp__(skc10,o__comp__(skc9,o__comp__(U,V)))))*. + Given clause: 40[0:Res:7.0,39.0] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(skc9,V),o__comp__(skc9,V)),x_True) -> equal(o__eEq__(o__comp__(skc9,o__comp__(V,U)),o__comp__(skc9,o__comp__(V,U))),x_True)**. + Given clause: 1384[0:SSi:1376.2,1376.0,26.0,6.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9)))))*. + Given clause: 1385[0:SSi:1374.2,1374.0,73.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(V,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 1723[0:SSi:1714.2,1714.0,73.0,7.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(V,o__comp__(skc10,o__comp__(U,skc9)))))*. + Given clause: 1938[0:SSi:1935.3,1935.2,6.2,26.0] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(V,o__comp__(U,skc10)))))*. + Given clause: 77[0:Res:6.0,39.2] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(U,skc10),o__comp__(U,skc10)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(U,skc10)),o__comp__(V,o__comp__(U,skc10))),x_True)**. + Given clause: 1940[0:SSi:1939.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,V)))))*. + Given clause: 1942[0:SSi:1941.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc9,V)))))*. + Given clause: 1944[0:SSi:1943.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc10,o__comp__(U,V)))))*. + Given clause: 1946[0:SSi:1945.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc10,o__comp__(skc9,o__comp__(U,V)))))*. + Given clause: 52[0:Res:7.0,39.2] arrow(U) arrow(V) || equal(o__eEq__(o__comp__(V,U),o__comp__(V,U)),x_True) equal(o__eEq__(o__comp__(U,skc9),o__comp__(U,skc9)),x_True) -> equal(o__eEq__(o__comp__(V,o__comp__(U,skc9)),o__comp__(V,o__comp__(U,skc9))),x_True)**. + Given clause: 1960[0:SSi:1959.1,26.0,6.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc10,V)))))*. + Given clause: 1962[0:SSi:1961.1,26.0,7.2] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(U,o__comp__(skc9,V)))))*. + Given clause: 1964[0:SSi:1963.1,73.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc10,o__comp__(U,V)))))*. + Given clause: 1966[0:SSi:1965.1,48.1] arrow(U) arrow(V) || -> arrow(o__comp__(skc10,o__comp__(skc9,o__comp__(skc9,o__comp__(U,V)))))*. + Given clause: 97[0:Obv:96.4] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(o__comp__(skc10,U),o__comp__(W,V)),x_True)**+ -> commSquare(skc10,W,V,U). + Given clause: 715[0:SSi:657.2,657.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,skc9)))))))*. + Given clause: 809[0:SSi:759.2,759.0,26.0,7.0,6.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc9,skc10)))))))*. + Given clause: 936[0:SSi:866.2,866.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(U,o__comp__(skc10,skc9)))))))*. + Given clause: 1979[0:SSi:1975.2,1975.0,26.0,6.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9)))))))*. + Given clause: 105[0:Obv:104.4] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(W,W),x_True)** equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(o__comp__(skc9,U),o__comp__(W,V)),x_True)**+ -> commSquare(skc9,W,V,U). + Given clause: 1980[0:SSi:1973.2,1973.0,73.0,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,skc9)))))))*. + Given clause: 2053[0:SSi:2051.2,2051.0,26.0,6.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc10,o__comp__(skc10,skc9)))))))*. + Given clause: 2054[0:SSi:2050.2,2050.0,26.0,7.1,73.0,7.2] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(U,o__comp__(skc9,o__comp__(skc10,skc9)))))))*. + Given clause: 2055[0:SSi:2049.2,2049.0,73.0,73.1,7.1] arrow(U) || -> arrow(o__comp__(skc9,o__comp__(skc9,o__comp__(skc10,o__comp__(skc10,o__comp__(U,o__comp__(skc10,skc9)))))))*. + Given clause: 91[0:Obv:90.7] arrow(U) arrow(V) arrow(W) || equal(o__eEq__(U,U),x_True)** equal(o__eEq__(V,V),x_True)** equal(o__eEq__(W,W),x_True)** equal(o__eEq__(o__comp__(W,U),o__comp__(skc10,V)),x_True)**+ -> commSquare(W,skc10,V,U). +SPASS V 3.7 +SPASS beiseite: Ran out of time. +Problem: Read from stdin. +SPASS derived 2209 clauses, backtracked 0 clauses, performed 0 splits and kept 899 clauses. +SPASS allocated 60229 KBytes. +SPASS spent 0:00:01.01 on the problem. + 0:00:00.01 for the input. + 0:00:00.01 for the FLOTTER CNF translation. + 0:00:00.04 for inferences. + 0:00:00.00 for the backtracking. + 0:00:00.93 for the reduction. + +--------------------------SPASS-STOP------------------------------ diff --git a/spec/fixtures/prover_output/ResourceOut/darwin b/spec/fixtures/prover_output/ResourceOut/darwin new file mode 100644 index 000000000..53261c6f0 --- /dev/null +++ b/spec/fixtures/prover_output/ResourceOut/darwin @@ -0,0 +1,38 @@ +Darwin 1.4.4 + +Finite Domain: setting initial domain size to 1. +Finite Domain: using non-ground splitting in preprocessing. + +Parsing /var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp ... +Calling /usr/local/bin/eprover for clausification ... + + + +Proving ... + + +Statistics: +Close : 445 +Assert : 20474 +Split : 451 +Resolve : 36693 +Subsume : 6359 +Compact : 0 +Productivity Filtered : 0 +Assert Candidates : 64223 +Split Candidates : 30456 +Jumps : 0 +Debug : 0 +Global Debug : 0 +Global Debug2 : 0 +Maximum Context Size : 272 +Incomplete Branches : 0 +Restarts : 3 +Bound : 4 +Lemmas : 433 + +CPU Time (s) : 1.0 +Memory (MB) : 4 + +SZS status Timeout +SZS status User for /var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp diff --git a/spec/fixtures/prover_output/ResourceOut/darwin-non-fd b/spec/fixtures/prover_output/ResourceOut/darwin-non-fd new file mode 100644 index 000000000..0abd9cb58 --- /dev/null +++ b/spec/fixtures/prover_output/ResourceOut/darwin-non-fd @@ -0,0 +1,37 @@ +Darwin 1.4.4 + + +Parsing /var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp ... +Calling /usr/local/bin/eprover for clausification ... + + +Horn problem: ignoring negative assert candidates. + +Proving ... + + +Statistics: +Close : 0 +Assert : 481 +Split : 0 +Resolve : 0 +Subsume : 42 +Compact : 0 +Productivity Filtered : 0 +Assert Candidates : 17783 +Split Candidates : 0 +Jumps : 0 +Debug : 0 +Global Debug : 0 +Global Debug2 : 0 +Maximum Context Size : 227 +Incomplete Branches : 1 +Restarts : 1 +Bound : 3 +Lemmas : 0 + +CPU Time (s) : 1.0 +Memory (MB) : 11 + +SZS status Timeout +SZS status User for /var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp diff --git a/spec/fixtures/prover_output/ResourceOut/eprover b/spec/fixtures/prover_output/ResourceOut/eprover new file mode 100644 index 000000000..b373c59c7 --- /dev/null +++ b/spec/fixtures/prover_output/ResourceOut/eprover @@ -0,0 +1,33 @@ +# Parsed axioms : 15 +# SZS output end Derivation. +fof(c_0_136, axiom, (?[X5]:arrow(X5)), c_0_14). +fof(c_0_134, axiom, (bool(x_True)), c_0_109). +fof(c_0_109, axiom, (bool(x_True)), c_0_13). +fof(c_0_108, axiom, (?[X5]:bool(X5)), c_0_12). +fof(c_0_107, axiom, (![X1]:![X2]:((arrow(X1)&arrow(X2))=>bool(o__eEq__(X1,X2)))), c_0_11). +fof(c_0_87, axiom, (![X6]:![X7]:![X12]:(((arrow(X6)&arrow(X7))&arrow(X12))=>(((o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)&o__eEq__(X12,X12)=x_True)=>((o__eEq__(o__comp__(X6,X7),o__comp__(X6,X7))=x_True&o__eEq__(o__comp__(X7,X12),o__comp__(X7,X12))=x_True)=>o__eEq__(o__comp__(o__comp__(X6,X7),X12),o__comp__(o__comp__(X6,X7),X12))=x_True)))), c_0_9). +fof(c_0_70, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(o__comp__(X6,X7),o__comp__(X6,X7))=x_True=>(o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)))), c_0_8). +fof(c_0_68, axiom, (![X10]:![X5]:((arrow(X10)&arrow(X5))=>o__eEq__(X10,X5)=o__eEq__(X5,X10))), c_0_7). +fof(c_0_67, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(X6,X7)=x_True=>o__eEq__(X6,X6)=x_True))), c_0_6). +fof(c_0_58, axiom, (![X10]:![X5]:![X11]:(((arrow(X10)&arrow(X5))&arrow(X11))=>o__comp__(o__comp__(X10,X5),X11)=o__comp__(X10,o__comp__(X5,X11)))), c_0_5). +fof(c_0_50, axiom, (![X1]:![X2]:((arrow(X1)&arrow(X2))=>arrow(o__comp__(X1,X2)))), c_0_4). +fof(c_0_38, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(X6,X7)=x_True=>X6=X7))), c_0_3). +fof(c_0_16, axiom, (![X6]:![X7]:![X8]:![X9]:((((arrow(X6)&arrow(X7))&arrow(X8))&arrow(X9))=>((((o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)&o__eEq__(X8,X8)=x_True)&o__eEq__(X9,X9)=x_True)=>(commSquare(X6,X7,X8,X9)<=>o__eEq__(o__comp__(X6,X9),o__comp__(X7,X8))=x_True)))), c_0_1). +fof(c_0_15, axiom, (![X13]:![X3]:![X4]:![X14]:(commSquare(X13,X3,X4,X14)=>(((arrow(X13)&arrow(X3))&arrow(X4))&arrow(X14)))), c_0_0). +fof(c_0_14, axiom, (?[X5]:arrow(X5)), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ga_non_empty_sort_arrow)). +fof(c_0_13, axiom, (bool(x_True)), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', declaration2)). +fof(c_0_12, axiom, (?[X5]:bool(X5)), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ga_non_empty_sort_bool)). +fof(c_0_11, axiom, (![X1]:![X2]:((arrow(X1)&arrow(X2))=>bool(o__eEq__(X1,X2)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', declaration1)). +fof(c_0_10, axiom, (![X3]:![X4]:((arrow(X3)&bool(X4))=>~(X3=X4))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', disjoint_sorts_arrow_bool)). +fof(c_0_9, axiom, (![X6]:![X7]:![X12]:(((arrow(X6)&arrow(X7))&arrow(X12))=>(((o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)&o__eEq__(X12,X12)=x_True)=>((o__eEq__(o__comp__(X6,X7),o__comp__(X6,X7))=x_True&o__eEq__(o__comp__(X7,X12),o__comp__(X7,X12))=x_True)=>o__eEq__(o__comp__(o__comp__(X6,X7),X12),o__comp__(o__comp__(X6,X7),X12))=x_True)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ax6)). +fof(c_0_8, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(o__comp__(X6,X7),o__comp__(X6,X7))=x_True=>(o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ax5)). +fof(c_0_7, axiom, (![X10]:![X5]:((arrow(X10)&arrow(X5))=>o__eEq__(X10,X5)=o__eEq__(X5,X10))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ga_comm___eEq__)). +fof(c_0_6, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(X6,X7)=x_True=>o__eEq__(X6,X6)=x_True))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ax2)). +fof(c_0_5, axiom, (![X10]:![X5]:![X11]:(((arrow(X10)&arrow(X5))&arrow(X11))=>o__comp__(o__comp__(X10,X5),X11)=o__comp__(X10,o__comp__(X5,X11)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ga_assoc___comp__)). +fof(c_0_4, axiom, (![X1]:![X2]:((arrow(X1)&arrow(X2))=>arrow(o__comp__(X1,X2)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', declaration0)). +fof(c_0_3, axiom, (![X6]:![X7]:((arrow(X6)&arrow(X7))=>(o__eEq__(X6,X7)=x_True=>X6=X7))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', ax3)). +fof(c_0_1, axiom, (![X6]:![X7]:![X8]:![X9]:((((arrow(X6)&arrow(X7))&arrow(X8))&arrow(X9))=>((((o__eEq__(X6,X6)=x_True&o__eEq__(X7,X7)=x_True)&o__eEq__(X8,X8)=x_True)&o__eEq__(X9,X9)=x_True)=>(commSquare(X6,X7,X8,X9)<=>o__eEq__(o__comp__(X6,X9),o__comp__(X7,X8))=x_True)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', commSquareAx)). +fof(c_0_0, axiom, (![X13]:![X3]:![X4]:![X14]:(commSquare(X13,X3,X4,X14)=>(((arrow(X13)&arrow(X3))&arrow(X4))&arrow(X14)))), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_commSquareMinushorizontalMinusglueing69766.tptp', arg_restriction_commSquare)). +# SZS output start Derivation. +# SZS status ResourceOut +# Scanning for AC axioms From 75e54a544426547d68ae8d6e8eb5b6cd90824aa3 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Sun, 29 Nov 2015 13:26:12 +0100 Subject: [PATCH 063/240] Add warning if javascript is deactivated. --- app/views/layouts/application.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 080b2a663..130bca270 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,6 +18,9 @@ .row.flash-messages = flash_messages .row.flash-messages + %noscript + .alert.alert-danger + JavaScript is deactivated in your browser. You might not be able to use all contents. .alert.alert-info Do you want to support the development of Ontohub by testing? Visit = link_to 'test.ontohub.org', 'http://test.ontohub.org' From 5ff37c8be8f111c03f2635ccabde3cad4c97ab80 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Sun, 29 Nov 2015 13:26:43 +0100 Subject: [PATCH 064/240] Add tests for javascript deactivated warning. --- features/JavaScript.feature | 11 +++++++++++ features/step_definitions/javascript_steps.rb | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 features/JavaScript.feature create mode 100644 features/step_definitions/javascript_steps.rb diff --git a/features/JavaScript.feature b/features/JavaScript.feature new file mode 100644 index 000000000..f2ac2aec3 --- /dev/null +++ b/features/JavaScript.feature @@ -0,0 +1,11 @@ +Feature: JavaScript + +@rack-test +Scenario: Warning if JavaScript is deactivated + Given I visit the landing page with deactivated javascript + Then I should see a javascript-deactivated-warning + +@javascript +Scenario: No warning if JavaScript is activated + Given I visit the landing page with activated javascript + Then I should see no javascript-deactivated-warning \ No newline at end of file diff --git a/features/step_definitions/javascript_steps.rb b/features/step_definitions/javascript_steps.rb new file mode 100644 index 000000000..ad0b2c46c --- /dev/null +++ b/features/step_definitions/javascript_steps.rb @@ -0,0 +1,18 @@ +Given(/^I visit the landing page with deactivated javascript$/) do + visit root_path +end + +Then(/^I should see a javascript\-deactivated\-warning$/) do + page.should have_content("JavaScript is deactivated in your browser. + You might not be able to use all contents.") +end + +Given(/^I visit the landing page with activated javascript$/) do + visit root_path +end + +Then(/^I should see no javascript\-deactivated\-warning$/) do + page.should_not have_content("JavaScript is deactivated in your browser. + You might not be able to use all contents.") +end + From 7fe36520172b8531583454a10bdbd97beb5855b4 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Sun, 29 Nov 2015 13:31:01 +0100 Subject: [PATCH 065/240] Replace deprecated capybara config. --- features/support/env.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/support/env.rb b/features/support/env.rb index d3d8e53d6..5cad1e1df 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -12,7 +12,7 @@ Capybara.current_driver = :poltergeist Capybara.javascript_driver = :poltergeist -Capybara.default_wait_time = 5 +Capybara.default_max_wait_time = 5 class Cucumber::Rails::World require Rails.root.join('spec', 'support', 'common_helper_methods.rb') From 9d140d3e57f988e98bc5ff62d831270e83ab28ce Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Mon, 30 Nov 2015 12:40:07 +0100 Subject: [PATCH 066/240] Use i18n and fix warning text. --- app/views/layouts/application.html.haml | 2 +- config/locales/en.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 130bca270..5174d9fc1 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -20,7 +20,7 @@ .row.flash-messages %noscript .alert.alert-danger - JavaScript is deactivated in your browser. You might not be able to use all contents. + = I18n.t('info.javascript_warning') .alert.alert-info Do you want to support the development of Ontohub by testing? Visit = link_to 'test.ontohub.org', 'http://test.ontohub.org' diff --git a/config/locales/en.yml b/config/locales/en.yml index 6ce54d260..26a1ba680 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -267,3 +267,5 @@ en: labels: proof: axioms: Manual Axiom Selection + info: + javascript_warning: JavaScript is deactivated in your browser. Some features are only accessible with active JavaScript. \ No newline at end of file From 7763b15e92547adf2b843a563f49a23221d1d7d1 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Mon, 30 Nov 2015 13:33:31 +0100 Subject: [PATCH 067/240] Replace deprecated capybara config the right way. --- features/support/env.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/support/env.rb b/features/support/env.rb index 5cad1e1df..a372dc645 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -34,7 +34,7 @@ def wait_for_ajax while page.evaluate_script("jQuery.active").to_i > 0 counter += 1 sleep(0.1) - if counter >= 10 * Capybara.default_wait_time + if counter >= 10 * Capybara.default_max_wait_time raise "AJAX request took longer than 5 seconds." end end From b435e4169b40e1f9adec9cd4133061b23cb2aa78 Mon Sep 17 00:00:00 2001 From: Eileen Bolloff Date: Mon, 30 Nov 2015 18:46:19 +0100 Subject: [PATCH 068/240] Fix step definitions with new warn message. --- features/step_definitions/javascript_steps.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/step_definitions/javascript_steps.rb b/features/step_definitions/javascript_steps.rb index ad0b2c46c..342fee611 100644 --- a/features/step_definitions/javascript_steps.rb +++ b/features/step_definitions/javascript_steps.rb @@ -4,7 +4,7 @@ Then(/^I should see a javascript\-deactivated\-warning$/) do page.should have_content("JavaScript is deactivated in your browser. - You might not be able to use all contents.") + Some features are only accessible with active JavaScript.") end Given(/^I visit the landing page with activated javascript$/) do @@ -13,6 +13,6 @@ Then(/^I should see no javascript\-deactivated\-warning$/) do page.should_not have_content("JavaScript is deactivated in your browser. - You might not be able to use all contents.") + Some features are only accessible with active JavaScript.") end From e4bccec4b65edfe76b945a93e9ad05f573254d29 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 1 Dec 2015 09:24:35 +0100 Subject: [PATCH 069/240] Pass the correct prover parameter. --- lib/hets/prove/prove_evaluation_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hets/prove/prove_evaluation_helper.rb b/lib/hets/prove/prove_evaluation_helper.rb index ba07d1c52..22a7369e8 100644 --- a/lib/hets/prove/prove_evaluation_helper.rb +++ b/lib/hets/prove/prove_evaluation_helper.rb @@ -34,7 +34,7 @@ def find_theorem_from_hash(proof_info, ontology) def find_proof_status_from_hash(proof_info) szs_parser = Hets::Prove::SZSParser. - new(proof_info[:prover], proof_info[:prover_output]) + new(proof_info[:used_prover_identifier], proof_info[:prover_output]) szs_name = szs_parser.call proof_status = ProofStatus.find_by_name(szs_name) proof_status ||= default_proof_status(proof_info) From 00d7f5932301d951da2b4f717fb740ed455b65bc Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 4 Dec 2015 20:45:28 +0100 Subject: [PATCH 070/240] Fix error header codes. --- lib/hets/hets_error_process.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hets/hets_error_process.rb b/lib/hets/hets_error_process.rb index 0299aedaf..a71578fab 100644 --- a/lib/hets/hets_error_process.rb +++ b/lib/hets/hets_error_process.rb @@ -1,6 +1,6 @@ module Hets class HetsErrorProcess - HETS_ERROR_CODES = %w(400, 422, 500) + HETS_ERROR_CODES = %w(400 422 500) HETS_ERROR_MESSAGE_REGEXP = /\A[*]{3}\s*Error:\s*/ attr_reader :error, :response From 1401d793f182b7ea756a2a0dee6b9777abaeeb04 Mon Sep 17 00:00:00 2001 From: Till Mossakowski Date: Sun, 6 Dec 2015 22:21:01 +0100 Subject: [PATCH 071/240] revised architecture overview revised architecture overview corrected png --- .../architecture/ontohub-oor-architecture.pdf | Bin 38638 -> 27967 bytes .../architecture/ontohub-oor-architecture.png | Bin 324837 -> 174564 bytes .../architecture/ontohub-oor-architecture.svg | 3128 +++++++++-------- 3 files changed, 1578 insertions(+), 1550 deletions(-) diff --git a/documents/architecture/ontohub-oor-architecture.pdf b/documents/architecture/ontohub-oor-architecture.pdf index c6e6d5c72bb6e17b6d233cbf04d8f4e3f6741441..921e2578bd445c4f95d805adfd6fc98ff34ba1d4 100644 GIT binary patch literal 27967 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 Mt^rw-w* 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 V+4XxQa`S>gAOHh{q_Py?e*pfCXE6W( literal 38638 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 diff --git a/documents/architecture/ontohub-oor-architecture.png b/documents/architecture/ontohub-oor-architecture.png index b9dfdfe3eb026119a18d92305b7d5144c5197601..dd9612309b6185bc0edeaafcd9d7660530d87422 100644 GIT binary patch literal 174564 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%0DpSUI_1oYnFY^NX<@Dk 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

Z$pM%dG-uPNK^ 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 literal 324837 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

Q` ztdbSpD9fZ~Mb{l9r|Z`8K{BAT0Yl#0_!ZvCzh3_H%b*$uaVvHG0pw;lQpmxEs8u&0 z!S+mpNUpxLzY6=m0j5i*exp}eA4yD%Ru_v4pmn%bs$neQN@;DA^>UG4@4-R=)NMOe zCO`+%^kFjU+;59O1)fL+t0Jl@!HPS*6MT1p?wobi((03OIH9$`7Pd9OOXcpbWll7d zCTWG)Fe}{Uil9@A(Mj>MK&65-~~-cb8MVQ zG1Ho9^n*%z31&AFWmmCR;JhdT zYg@b?(!v|-V3{&%2~0pn@WK-wM2ut$=adj{{<%-KE3ED=mtE|zBcGt=#}wUj{cxO# z`5M1~;dp5^d;hl~wGe}dW}XU|uS5LR1BX=+NHS?}c)6!BYO74kat-qQ*ax$nAW+^A z$0Sku2tF4k+qb)|^2h0PRc5w(<;uuB>iPo_zT9hH4?nVYx5Hi&)Pp>*dqA*1H1^1)1fJ5iQJ3fukcYuD;mrp(wn*7jzbE<$N{LH|MavehKx>$=@4tu}9b4)0os1 z|2WmPOMGyG(b~z)FP~1iW4%=FCyqauc2Xl}I;TeR+d_d+*?3Pu-V1b{CJjo^k(z?5 zZU+Qo{8Wr3&S)M=BG_{ETX^K$9JP&$mIhLd&4@$K(l;key8l}0pI!Q9SQMxClLRip@UQeZ zOmai#?m1h0!~!GAZwHt(SMva)`UA^P0>H_Xk-@e-GxMii-Jw_~I823xXDz zcarOQDDq<%T>r^5Rz;`tFN_5*g+Pa2Ykb3 zW2%pW;5CaGp=jC0)o<56iT^o2I%k8=tuQ7hmRb^}yq{t_9FflHglmQ#~Iye6Sh5k8p zQanERsnJUwq@_2%E?s%J;i@OU;ahrE`!tMb4v)ud88dhuZ2MA@Qn6Cc^%1sfs{>Dr zuQ)vZ=5T?*^Imn*5ux>S@*r6QW}SHqB+x3`#EjU6a+%mhdc$+#!|nlIJ-^K zqi(wP>*PA{G72vK&&D0{VxKOzoY+# z3{fj_{Ft%3k)9G2pEu?0TP=Aj&_~7|otya>W-~MCfD$deoDgfoQTDAlzKb3-4(txM zznVhF?wv z?SqQ)9b8!BaQA09h~_XigX1IdBuSQ2rU$zN+^PLjPBoaAp$VE@v2BgX2Roe3T+;s} zu7wMk1SaoJdF7uCde3?Kxub+ae9jy`@6;wV7T;AgC1vLnAG};Rf`@dqBdPDw%L&zBvPr4>Hl^Y;RgO8-hW&ITcKfZLYFn-!jWI3(&`AvWX!J@dHf^eb5 z#jovURC6)jnY3gF^bYi;YEbRKfD!^DUP#D`sEs=R7T6?N^8ND^U3D4eT6*|IQ{HAf z&Copwn;p8nF^jjovd^BV|D8WR0=TtkCv1=~ru+;^wRpL_F*b->ql08{ek->gX|(Xk zZ09OpJ#{=`vV6Y?C(C8c&byJaW6gLx31gOR+!~%iy7NiPnCmT1&8h+uj;P{^k{=y? zetNEk*3t|~X!oJ4gIh!Tn4PKFErKTWf(PiG$gHgpi-FBnbXMg*sE&2b=q>$OCyr3GIoA?K6>K z?5v-kmfTe74_lVxgNsy@jv4bENKIKrT2E4aFD(`%>LIe@gA{dLzA$vpCcHk5d`;l| zZyBbKu{GaOTtO?Ng01#I;+Z#>#w!chGM;g>m6eN{D)u(tK~RFk>BFcGQQy*`BI+g_ z#++*n35@n;PnXoqsxGa~r8|}A{Urc-VYWkPOuS|U4M;C5*H!Fi%)8Y#;>xSACbRT9 zCYWrFoDaeab%ypTAiHmWz6vdBOV<{lc}&m6?B*1MmM7AQU}X#N&Z_Sv`;Rc-xpitF zZ-KxBI@xObmtI7`D6z502+7-#2p$qUwvT+eDmz3NL5PstMLtNNf?;poO!Ym-94sN0 zS|}PL`o<6XC7m~MjxU2j;-8#KP6d*A*b93z@uTGFRgOE!dbV-dsC@C}0#E3W1f#JT zYxgq#)GGAF3sZA}&}UjX2QZuJ^u+h1Qzd6W#R-Q7Uc*lf!Z=cWtUK7ZIvYPmeeLNd z`?g1UD%ubGC5msE4I^R4N!MtH<$T5ZTt!dC8=Jif{oef=_hRqdaCQ|I_qb#~gs5dQNUbq#xA1_7%zXyY7Zp%;9F* z1%oLQWAJy#5;UH>-8d6$i(Dzo^%ngXElXxyB`~>0YO424*q&-FXF3hi5F5sqOxQ5K zu=u4A9p~`AzJ!BsINiiOQ$8S&pd-}j+*(kAuXwuVAh*G{>mc4%Sv*fzmgIA-c*|*F z-sg5B;up5)E+7yEHy3=5K&3*Y3i`D#-#-49-t%(EjWRHv!bBe5 z{&cQ$TD{b{$MV0|k3{4oGyv+QVMJII^~HMkl7T;u-8s5TFBiRxJ}W>yj1M;6py!OI zZs;iUgRpX=T4PA}pD(-xA#^ZZUn1BhB_EFYr(QfMk~Z&T+O7IZP5R&kv#J|kUz#x7zHQMUZlhdo>~0g|nn~@i6}8;?3UmuKgrkDFZXaMF z@6FuU6@+cDrDI!+L&Td+T7`Y5Qx3hHXxBjQd%SQ4)AV{~Q>`dpls|E-2kK7@V`Tw` zLv_O4=l`*Yetk(vf=cH;Cgozk1AJ!my^J-O^uB~HnYIB%vaHQKPZ0`cv&4VnVtqJ* z>~czP^sOo;2fOv&e_&J4v&Yx~ZQcexmXhWDNz9q`# z3+fnvm*twU0}2?0T_@x2LP6r=`u@Of8)gTtwTua~=$?`8<(5`oUqeSM4M5S#{}iJ< zO$4C{RDrIyehiVew1MQfGyL_=Y6PB``Q?&mNWGM?*@^UdwxD@GHEZ*vv5x-KXJ$!} zf9bG@>n=C#2t~?%ldHgwkDNQjw`6W+ju-cBlyR2cU@?SMnTh_+-(CQ-DnY!mgvuM9CPk^K`*E8r)8)jTKr})SN;UciAHIZLHI2%uHLKcZ#e!!q0!7% z`{skHPf=tb%m#0bW-4n>`9@_(@PQ5gkooUTfdS}Fb~LBfLQyFLE6vu!_sE1LQQFz! zGZ;#DUns1dp!tL6h(B3PKg>A1 zRlog^q}B#o4!icfl+Y$GHj!czPgrd1!7K%!6e=SI@K%bN^w zH~uN~De0^Nw!`5kQ~0+Y7K@Hc$n8lCp_Lnl`qbd8U^#*#mD4Q1Ty37*Ja zvc7vgBz0STouD#dQR#5+l}BUSlj*5hBTYsQePn0#)pPM83;R4&-k2; zCq9%cwc~qm0R|_liD{=~_~=-ifWTlRj$no6BxNdi^`}CSH$g|m$3X0J-S{vDN`&wF zdy@O$Dr3m-W~S-Q30ZMMyD437$heGKQ+>1g_cCw%z?bc|>IbpjpJ9{u`syvUgHit^ zw0?Bq2MU1G3ZB(F`rmYr4eoGW1(}%Py~LnqClRKpN&A4%Wr-~|N&spTiE-2;guh!A zRr>@hcEIkO4G%LsW$$vqOAHE=Bss(%Wp|j+3*f0eIL`+UTNLFK$TCE_<0)2+)F+So z7;&F6WZ4t7C%1Wz(za(gQ^Rz^+kRa?qTY5Y5FR`Oze~T z?E9_Hx^L_60HAg)#NjB=%Dpo2gW?#|i<=n9=B21MBB*pg@4LSMkrL5-!9m^eupDM{ z`nI^ovfSL(dG$MxJ}B+9=&;Dzg{cnAq^eKh5VyE`W`77_P9&1sTLnLkKnTCL{$R0K zh1#F(GVx{kiDFL!-(FacBUa#fYKh`q7;KWF;fC?D6}pc}_^R=;+mz6MEOq6KBYA0R zxyw0t%yN>1+ei?8*_#0C0MMiC6|2OLnL+)aJa3!jy`>X6Itc1#Z%-0LE+N6h@=074 z8iu(M9aHY=Q*6##W!8K5nEv(TA3BZY8M&rw+{b}V`7c%TGtk=Ssa^Y`{jh`GP}69q z%KBa*2tHKUxBxo-?$o#OfRG7a%i6%VpQ80%=yflfLu0Oq3CfDTHpF44LGwmy>aWGB zn+nuiTJQfN7P_kQZt{&!oruH=rb|)cDL|IgDiLQd+l#C{cW*9(qul!s+TKLG=e#H{ zj9)joc0R5pnHhnV zk^YzAWEo^-c6-v!d#t0+c`I98tL&lr;Jv~c?JE0r-iLd(>(~?g9m+z|wQCL^>Qx>( zN7hT3n=#=P6H!nm_kJ>~0!0bl!dtog%$4D*bwfA8eA}%wVdad^+Yf;glJ}h1YXQuM z;|bxBkc1K5Cftp#@0~xEam;AyX!GI=A;;TY2khgL*N$9duXcqO@lYnu%m=)ARoYzI9tJvLJNGy|BnZ}+%gY!>XbFz2;(M{Ftul&+UfH`L1V~{Ifcf7(jqb?-md42Y8L~NgDdAE?ZN`wzO3-{Us62wvF!_QCgFp@0@ ze`24T4%{Vrq5P0)d`cz9Yy z7M`6PFDMPTp8J=)W|85#qfr;+#$rsj*n8?&2lLfr%|!ZjT_a%qCh;yOWc)Ao-ZQGH zZF?W4D5wZnL8%r5r7OLI4e21#t0JLyNa#flqSBNqz4y>Np+r%7lM-qG=@1AdKN)q^`|Ur*`|-EO0LBQ(&f06v`OIgQwH8#wjMk@-=q_z<>$J=i4r{2f*n_4e&iR5Up7?hCrB!vE&n%UQEhCNB1K^E~Qj+V2IU98^UZX z-OJ#ve@d_nM#Oe1WN_&ktg&JA7S{V_Nzjb{y~(R3S;Qr;&a96Sn?#4R-*{Ua`0pyl zvj$zR8$0IBxU|AY*=I@SwkMM3v9UtrP`t=%A7%x&ZTBLON%yAFrk|q49v@f+RUeQ| zFOaoK-0FAAiRbty{$qLACHn5_SksAC^ZtTHF-|qQSnm$ww+0#gkC-P@Ao*}@eIg}} zsLWTT`d~T5eTWG6#!`mbpaifZxifdEdm@}E5$q|Nuivc>KC$EzyI38&#Te7BbIx-d z-J)5eM+>RsD}K^h{$Q>5YR$7ry=SX2-wF|-F8B@-Od|T=W^UwxKC0G#SPh9HHqjKI z<{M)YzGd9mhCMF&AD`n6o;TV(LV<7WqU)pHU5mm;3ndP#N=Oyb<;P@kG|)YqnXdtf z)g;lOopC+J?~dR0jT*yvQ~#yff59n8EeRG*eYR6|%PuL@B=X6bs^%=pY}xy)BlSvb zEP>jYNQxR>lf-hN{6=01qKLFGD35%_7&6x&`}D$?TtUAAQVcC&wUCDe)gLp&0S817 z+1X9*O|eCtcXX<_5ixn`1&^bjIA-XkB^AA|+*}fW88z`5H66ti4a|-LPQI>H>LSl+ z7WN=)Nc2W@rPa`*d!eglkf`H&CvjfhIeqy(4Qa}MQ*R3;x4$Bn=K_N9_a48UT^^BH z5t-r`v>n&odLbS1yrO|#8+LttHD!px7Xc%}%MNn1*SgslQ0CxI{j%*-&7q74J6YKl zYCn~WFeliNc0F6~f&njREu#Qsm`H|d_bH?wTeACd?kDuy45($F4E$QT=uMMV4&X~p zx0t$d)w^5}V{lb?Tbl_d_Ll8Sg%kmGFsnHzN4h66k%dyitN7%6a}nauXL`h*ooJ)b zyAY5n{*ZH8+;AleMrp6Ds^n|sS5)f}t?WKGVD(ZmOr&!$vuPsCznNH@BH@}gH4)6n z6r%3;N#+iWo3GoE4;)qTGWP=k2Z^U_?kyLxo;40paOj3?ZQ}i7ZB8*nq(MY@NPN!z zcb#?>UT`BX7uManXL_q}t;JK7RshAZJ1bf;ei1E82KAY*I`8OZ8{i zuJ?VB#P|PXd~?ejW}3Ix78k28?s67fpz>!~z8xM)oNUzSeYESY^7ui%ob>tKQzQPG z3!1L_OvEKKd;^>dD%ggP!&9vc#@5F5g{??T17C=mSL(&cae? zW^zn9x`~@@9vdUpM2T=Lz+KrWvL_|Gb7`dCBmMe8U-9}yHboW z^+Qc9!Pm=l>e+6_4zLqEXe}&bF*NE9gF1AA5b`{_ZW--V8M{hR$5%X0jrMW_kDf;R zvps-6jrNP3J)ZuJ)zj{cMqdekOD-}h7(V&*AclL{Ma3D|~6+ynVe8`Je@P()}_&nSo~=e>G18G?%-D+F-=HK838S#A+kYs z(-|bFom_j}@pqoY1-hJ@-A&vL$BU;~mo@1PzpT2ro%A%h1ZTKrTvkw7i04leUf+J0 zS`44K{7DgHj#a&Xuv)>A7JTE?8JG#tTZnIwX^vL+$af-iHi?zt5_T0#agd=8yM~>X zJ5_FmODIC>vUZVr`syrHz`hvK)9w)5I+P%AgGPXyoHg1Fdl{L6bl|4W(@=h4e%1Xr zc!3VwmCLs%1HKnOy=L)9yFOr0Sy`tj(gkTpy8K_n2V`~ci^Ff z2gKS?tuVZ>eYk;d@?R#_kH@j*#*?Gk-R&Nt{{w*M0=`;+mLj*vjHWIs2}Uvd^$R=W z_(bb+i!i10%dg6~CXl^dRXKS6{m2l(kx0DPPClm&Z`dFQJ&#s}^^b=TW!I0CtMw3- ztS!vY+UFnXvQnx+ZgSkWNMAi#g3dI=+MH&HDw){BPJEi)S`9WNQ4JCW~ zI;SFT3&EX9Nh!UD)o3xx(dc%=Hhk%nysqt(hEO>R*OD(g{ms@1zd(v1Hip=dRemUp z|H6U%g!PsUX%5;Sg_C7v173X|6ov!CgOJ~q{XS95xWTCXM^AstTfIRhw@^4Vc)yI8 z+l$F`y-P9S8medN!}>d^KN=rpUQ>slU4ho;?Cf$kd}r!48RH zJ0C(rLxX(16d0>gy(Cn`U1`hAluZUaB)`#&S!<2G|FC5|v0@hM*<%COZ78pLbTZQD zb26hWwpwmxb)Torz?MMxrmBN0jw4^HaS?J0d@y`BIUw}YGvDjhkkIU862GRZXNaey&;&_lHzx5Qi{H z?3V!7$|&r$zjb;1h%K_F#BGFwqI%(NzH7%jwewO_7s#kS)CuXZBnsqfKG;4Mb6tvM z^AK|`j7=Ozp`b2Y^LGRy2pJbrvDa`H<+o0ybu^@c(-n!%rEf>&Yx>Z%v7i}d!q0`7 z;@7lTYrbzu`>Ms^7yri6g|=Qb`TNc{ABpw{Wd*)Mxb|zE`d#GNCxk8zj<9!O z!p5)cOagOy(zSLSW(0xU_FtBl#E0UR_hVpRZ&}Y?GCjA+aryspNKcfD#I6BfydL<*+1?zz4MC`*`g(vb~ z8uEFNuGa*+$S<9*Wqh{ct_)%@4>FW4Mcu$QG2htx>ckW#d;GRcMXl9ezQfH(7lC6-Ek3x>pD~Wz41JitGf!8&2cjdlyW2e?Q8bsI(k!j6zQ*SPX@f0~S9r;~;K5%#8 zBCL_)fApZT6_c#&1ig?C*i{bRn{r)Mm5Zp;`ho*-Za1DK@AmcCLoWyBw zaw}?2vGzcdrYE_7EH%Q<1OT|dz~|@zS#Dww1ct5r4B`Zx5eHTyr#>4U;)MKiDnn<` zUX~j@2=c+dSk7n#y>^%-+3v?;MNmC_DHm&=8H7}kXvL1S$k)aExYa1AL;Es)0#7@B z6!oCzi8r?sHE_C6B-HPY+0MGYA>%jTfo9Hu?85X0zu(kz+%ErpgUtbnzuTsMv1<3% z5Is_{Q_-F%bPO3PKT6m7Qrto{$bA~97ASiVlloh_G_WB~5BkqGl!&iVYO5AxVe*Ee zM1QxT@xQOEtfcq5h_v$s(vKH9&}lYuy3WTY84PnCu=eh&vNkvBxlBFr-C)Szhn}$Y z)pb&1qktw|L&D%XwP)^3?FTPFyg;QFaF6xXU;%j%L`QNuRGh@M8Ev&e`@O-hg6Zyt z_Um=_7~jvaHl%g;!bYz%QeCm+RURO56XVV72HA@<))3NnXV17S-?P4Yr#9Xox5qSl z9lU*&$W!q99#w+Id`+I{yHAoin(drZwf_@~jiwdh1gtKwE_-Q{E3R(eMRr!+!q(a| zt$CQ6cXtL4^G?4(>SjKMidmt0PNz`4`ei#xnW}f;L=8gFm0=E1QBiKP9S)cAw>#U4 z9*f+D2_hk%*7k1b*&@7b*x0#jCMe22d#({+5)NRJioxjam81?I%|4>91O~)q|GJru zw${U=*(rbN2HCoradxmktp`nYO^46gr5Cg}@QKJyYo}L_0LgRZv`lr9ShrU#z^2hI z-!6|GxsCN!2sL?Jk7XP8vKfu&v}QBzND+y|7+2mdv4U3&r}AtbI9%i{h*`)VtalnZ zo8i%OGKZa;|D5GNY(>Z|OO1%>L$|G9JcvF2f8*q{Pb9D2=P11f37&nru(Ru+b4K|S zE}qDm^gI=O1fvAe5arJaRX?8a&H86j%(oVQT=pgHT6yfg=vi$2g9KailbsdqM2{KD zY$(#56+prDzuc}ufAc@7uB@^jX5c%}PgUK``TEjJo(aV1l;^>j@VZme?9D2duPx^B z-GF*tHG#owtGJESUee|$U6_;bJ&i(Se=|`Fno=+O>m&P>nrrmmeL1=~A0Y83LGd={ zR_I=EV@Rw4OT|tD*+iK`LkFGhKRT(>wgfun`^rK=~MV3?8=`eooAs;h{53u^5g zNYnqJ8{1EeV79fs+8gdanPlcbIi;7%;1?#^ffkZem6Unpy)w0!(%jFW_R-?s-sOnb z)i3J_`IGoo%2U1TTwYO;$Y+f7{fTsuU_2Nve?RW9omscl8BEuutot5;Te*C>dL~Z6 z%?%YH!8l5sAoaTS<4+qKn;A=f35yHgFZ9QyI6ea1i}u#dxFQGqo44i$goOk ze;-CKY#i8z**srH23wZoY#aGiWYrwN3#mVk(EF{vSzahn(8|vFK70qs^Tv|+JZT{Z zcbY8y6MbP`42c^T%@QK;ZC(^+0aSEY1aCl{czSqlj+hqE8BER;5$Rxcfm@ z-TLf{9XCArwc3&5QRNqLeSKQ9S$oIW)NNw-uMJT~al> zYtJ$QV-d}6-zX#dCP*B5oBN7W4T5KBNBi})J8L)EX}+!YowE0O-*OA~%<8T0@Tn>O zDFYwRE zat{omvxp!Nq};upBPE_5GtiUb{DI-KBR9H$th=D}?Z3@vix^TVQmkOy7LzybT4i`^ z?c%e`WFP2Y9}q>fBndWwu3g{b^y^66%VJ}8t0>?iuS zOJK39$OXRu5O{_@Cp;Hs6~lh&lRb-DwS*^7p$FUy?szmGj5*QtTb$8yhd}9)qoA3Quv9 zP~9b9E;LKRS%{}Qp}7_B`H`#2OI>Nq-|A)q3 zy!c7}@mt>%H-BEWwI*j+(U$Jo-8V&hSbEY_3Bo>r!YTwBf-1tS9_v`RSZNkc{nWFYP4zJx(i2`WoqG2$ zb^U%oQ$~D_uQtklSln12Nzj;gz9nLb2J;)BwWxwPFo{87-~<2aeiP@Zo%X*_cCbqV*g5zj6t47Ci!i1D{kN_mwm4R zP&Syu;y8r4ZD1RtVlsdC0@U_gxa=))JLSKDg%R{nJ-tLlf8_-S;Owy+U3?*?yrpdGg5XyUrA+uVXg}v=bI?cSOwjdXF|-_ zb$UCpA22vxdn81!Ird)OfYU%Y`916;=nlpF4s`MH)Mub3_LNz7y7H60*e$9-9-ZKUd;g(E(-GzDK@M(>~Nea zWlBn>5uaGN6%;#iANd3Day7|at)lOfA5QZO#OMcedQbYi&*(6TT>CttrjfwBSFw~! zn26q)-S4~$!6_v~lGc!T*{g*9d?jgZrSRVQ@GC7^^cOrhmPlNyY7Z`7WkQHI>E~r) zU;aC$3va&r%86O0=nG13Kwt_)u>_pGVC>pk?RM(pEg_Vbi#N7`PW!6%0UxB&=J8EF zF&g2UX_`WRD%j7QK++^11S|JgY~q>OVZTsT%9Hx!7{jc?kIx)cQnh@AbyDFwj+?}F z&dT1K%sAs4?{^W97c(Y@Zx`ta48|5z-xDwE7a11KQDYn6feXxMnAWcGVVKogJ0(dH zXw~wA)H|H1{i_4Iq3rsHUB=8eW{)xhzdwUDMrAnZWdwCuCkHP)ydPCoy=#@#&i#$spwD8wUy!!&w4}iO3e@Kjj4q}n)_ucBxYJtiQ#eEP8)Q8=3+}G z;Ni;$>&jEE#uhc;+$zIX38CZn1hd$-dL*W=zz8?2NeHy7_X|9Bk;vED8s-H-4PU^+ zpO?W;0G*PG?xXsL8X#HFFcr7$%B9RBBLwp~r+qL0-H2sp)lq)^y5N(qt&;mc?*qix}E(^8dv|kk7AMdI2maXR9 zQ#};bKjSG5kuTb>L=-8xDRYs&>K{Kcx)-W4CIiu^kq-4-enoKiN9K#Fjk z4#mD4Peo#7Oy+7RUc6AX;Od<*`igBJ-X-CcGFS99@Xhb+e4bE7%PVYt2set?CBqRN z6T_()oD{bF<^wpX+{?v|t{mBq` zO#X}2GO@%bJk61nwkGAhm!qkrhG$Xa+|YKGK0kGi??C4Mx)x~YCU1k(WD{9n4W6Z6 zm%4oC&x)(QmPAm5asmbf+VF(e^=oEOg@)fvX6$|*>ou1+E=>ai$}4sLDUNM8K9@Bv zm9(>saTj$au{Tat72b$cSI%`2qO@S#KJ%`g+s49D3{Q;ZqTYyBWd_ zC^TX-q3K-8P;VB3hvfJ=KO`GZe8T;XH0)he|Ic`3SJl~g5iM`ljGo(+H5 zV~v=FxG0$vw2cgPSqBa=esqn9Wwe2Wg^%}dbWgcC4?Q3*Ax64R4cmsr$Pvxgp$7ARR~JZb9cQgOnCYoWkpV1oZNR1nGK4O%{Z%yGUREC6G?mgAf(c7_oEez!sL1RZsMuv)?svmzgkaA0iyPQogIGvI(UoQcwYk~;ZT$LlfB!YNR`L-`#E{MWO&9)!npnjeJe6kAO2xfqB~XD9 z{wXW9|6@hBQ(^xv-M~CSqJ@PQ9#52AIz+Pjo@m3?c*~5!Pt~T?hrD|CrIY5Pv$>v9 z>6t2lHd|%4T4v#rH#2D@(xAEwGJIn}EUvUSS6|Iuo4OFr{o*2_-w-S;Fmo3XT(;ut zK^rN9I-`wZ>1T$UT?gJ2yiwJv#@WQkp%0CyxDoMiq=uQX)6CR{yvQiJ?Co}nAmcYw z_|ckw6cXa+bZ_nwOpC&BsiT3!dTiSdIP&+t|1_x)Jf}p^c*^BcyFMK|K)3n8BDJq3 zvKMGywBPoPw33D|DsG;A=Tafzkl0&Fm_JZn`{iy!%s|FN-4{lX*|jM%WbyMBE~CS9 z_=znu=%|z3QwHa|{$$j-OX71Cnnl{(vj?_jSf_gE8=oJY%GOp^hz1b7N@k*nOX6!` zHRoP*43%D4EHKf8#gIx7K|_+e^DDWJ^P;kSRR946?Qpt>iCAntAroAqnoQ_ozIAwu z_mi#Atk%nG_KC5K`dA493~MZH zr>i*g?x+pDU@8t1*(3Wl>R09n=lM5-DBuvifpV6)#>Y4PRn@}K2VJfl&mAU9A8ajT;$)cI11ibmBUKX3ZGj%6Gx7eM1GeLZzSjCH;2Q;h6edQ!x-h* zN;Dm^5VUz*&kxQFC1(q7;l3N$^x*j-zxw6x2`&&UsjAXo_tUqx1avdLRm6# z48#;|XoU%_DJ<2YsCmLOCT%OQT&ec&#v1n*cdyLToD1OjJQC zgLoUIKs#jr)@v;Mv{MbHmgBSgiRSL2OUrf^)?kPp1eT_fRy|x$K$gIuN!~F^@6I;GuO6zulSkw|3 z`uDQePc*L~q|XF5f=l0K(7!0l9b!#Yo9TAO_gaPj%2lY=OiV;b^|{{6<0G*WFflLX z{L^5QaSIT#T*;ymGK130$MGrp9_1ecy+;t+?ChzfeY_d*DA1} z<3`Mdn%(%0o+o@5^5FCAS^yBVr$XPoci!OO^MJi?!Jz6n0!0*|_?>dLtNx*~;m+Px zno`7}6os*7((qA6%;uq+4+61JLZ9+)0$ASg6ZsM89$V-q=o@Dg$Zd#YzRC|_!ruud z;mpwtTYHZvx?MwN*x~WUeN$lyi*)~+3r}vL*SujaAWvx&ezMWfIPSnmKN`EyT0bs= zr3QN$ejimT4*QH-QN&j7KL@@jAtM5&BSg{!EI~(>!QnCAWDa*%x@M4!f-i?7Q@Uo+ z+=`_#H}^NMbFL2{q5>|LL3vp!ezPuzN{w;0s$-A|*_)!P!S%G(gWUcKuMk93y6=lk zLafcu>PAk$-ks|WJiKKxNJAN?R)%E<{08#AbeNBvP|6^30qs?e-=q#czX=vsk{|1s zhKQ5C`Xn~=U!7Kymt1V>)XT_Tf>x^@C_Vth5A?-yATFp|`!q>kZ%7ZrXTO_#%^!sG zWhOZwM_HqKp2+=^va^r`WSD&&d*7tllVbNYFMPlO>=r zvE%_x&1#!;I4!QcWEh2Gc~VcTw<-nazhuSELR6<9Pl-e9my%> zaO3CcR^;C24uwt5+_Bt%Jq?|d{S;K9!Sl5CK%DR3mc5t*{nPqSEG#>cu(cIVy6xJs zw`E|--RZ8>QR9hB2-&*WG}N}EzaMm-*VTs1U%ep4aig>>;|ocRiwJW?3Ot!bvwI3#D6^?w`#j(Yb5oJqdzDQhOR+ z{hz$K5#oqqXUA|xzfH}cq30U-;=*w+s&(aXBGudRBn-3I0M1mZU&|BtW9r`UUZ}c; zIpreagctL>f3cz#EDvM_oj(s_k*;JqJc^GEZ@fPwQ85a$Be#9$SBQ7%Ym78=6!dN>X@tu2^Zc^rm&u_>uPX zn%H8O?$lr1_S$MMq^-jduv3GUXa3|QZ)u2Z52!B?8!_-fI+=ZOepS&RWxK2)E_qA? zkrIje6v)s_XhE3ck4H`KIem7;H)`*+znP%&Ixk$SdY2#lDzrRO*BQN!KuD?kJ8XJ$ z1-(BjSl3Rddj`tU%y>(i6db@wx{17(v6#FZK8E4D5P7AG7d%GMw-NM5r-V=KN*i~9 zXt@cwjEvJc;SO@29FzMRtl@-jj2$6HY9Jdf3Xm$1ggW65tavp+YZC=pV)}p^v|pC_ zHO{JP{Tu)HiTL=8Ul_L)TcrrlTpy5k3X>kFE*tlf`&9@$(^h}^;&Y>T7@j$Pqzv#y zKN;LzGuCxCn2$>mr1EyLjp|V}qDPtW+pl}FJO*LSRag3T^^}$FTNH-wNgFfANcAu& zY1x*yaa~0tU_7n?L~#NvUZsj4Qw{PR77@5cy6M9e14#5@8fYRw ztK2@DCVtI8%D>xqSa1BOMdNML4O?2yR#+;JWm2jWC)U&D3NKqJxBdO~pGbEdBiO)! z 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 diff --git a/documents/architecture/ontohub-oor-architecture.svg b/documents/architecture/ontohub-oor-architecture.svg index ac19bec6a..7d9686fd3 100644 --- a/documents/architecture/ontohub-oor-architecture.svg +++ b/documents/architecture/ontohub-oor-architecture.svg @@ -13,7 +13,7 @@ clip-path="url(#presentation_clip_path)" xml:space="preserve" id="svg2" - inkscape:version="0.48.3.1 r9886" + inkscape:version="0.48.4 r9939" width="100%" height="100%" sodipodi:docname="ontohub-oor-architecture.svg" @@ -21,1643 +21,1671 @@ inkscape:export-filename="/home/till/web2.0/ontohub/doc/architecture/ontohub-oor-architecture.png" inkscape:export-xdpi="160" inkscape:export-ydpi="160">image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -T -o -m -c -a -t -/ -S -o -l -r -H -e -t -s -P -o -s -t -g -r -e -S -Q -L -image/svg+xmlF -i -l -e -s -y -s -t -e -m -R -E -S -T - - - - - - - - - - -A -d -m -i -n -i -s -t -r -a -t -i -o -n -A -p -a -c -h -e -/ -R -a -i -l -s + id="path143" + d="m 17739,7350 -2261,0 0,-1270 4522,0 0,1270 -2261,0" /> + + + + + + + + + + +Elastic search A -c -t -i -o -n -V + x="17087" + y="9476" + id="text185">H i + x="17544" + y="9476" + id="text187">e e + x="17896" + y="9476" + id="text189">t w + x="18073" + y="9476" + id="text191">s A -c -t -i -o -n -C -o -n -t -r -o -l -l -e -r -A + x="4354" + y="13445" + id="text219">P c + x="4777" + y="13445" + id="text221">o t + x="5129" + y="13445" + id="text223">s i + x="5446" + y="13445" + id="text225">t v + x="5624" + y="13445" + id="text227">g e + x="5975" + y="13445" + id="text229">r R + x="6187" + y="13445" + id="text231">e e + x="6538" + y="13445" + id="text233">S c + x="6962" + y="13445" + id="text235">Q o -r -d + x="7457" + y="13445" + id="text237">L R +E +S +T + + + + + + + + + + +A +d +m +i +n +i +s +t +r +a +t +i +o +n +A +p +a +c +h +e +/ +R +a +i +l +s +A +c +t +i +o +n +V +i +e +w +A +c +t +i +o +n +C +o +n +t +r +o +l +l +e +r +A +c +t +i +v +e +R +e +c +o +r +d +P +r +e +s +e +n +t +a +t +i +o +n +W +o +r +k +f +l +o +w +P -r + x="7495" + y="11666" + id="text731">P e + x="7872" + y="11666" + id="text733">e s + x="8185" + y="11666" + id="text735">r e + x="8405" + y="11666" + id="text737">s n + x="8718" + y="11666" + id="text739">i t + x="8875" + y="11666" + id="text741">s a + x="9188" + y="11666" + id="text743">t t + x="9375" + y="11666" + id="text745">e i + x="9688" + y="11666" + id="text747">n o + x="10031" + y="11666" + id="text749">c n + x="10344" + y="11666" + id="text751">e W + x="17249" + y="8495" + id="text763">I o + x="17406" + y="8495" + id="text765">n r + x="17749" + y="8495" + id="text767">f k + x="17935" + y="8495" + id="text769">e f + x="18248" + y="8495" + id="text771">r l + x="18468" + y="8495" + id="text773">e o + x="18781" + y="8495" + id="text775">n w -P -e -r -s -i -s -t -e -n -c -e + x="19124" + y="8495" + id="text777">c +e I -n -f -e -r -e -n -c -e + transform="matrix(0.10593701,0,0,0.10593701,-465.60477,-473.60992)" + class="com.sun.star.drawing.TextShape" + id="g781" + style="visibility:visible">F +i +n +d F -i -n -d - \ No newline at end of file + style="font-size:635px;font-style:normal;font-weight:400;fill:#000000;fill-rule:evenodd;stroke:none;visibility:visible;font-family:Arial embedded" + id="g147-3" + transform="matrix(0.10593701,0,0,0.10593701,-404.90076,600.93197)"> + + + + + + + + + + +git repositories +API +API + + + + +SPASS etc. + \ No newline at end of file From 373a1177af3edc925756ab8c9159896334dba879 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 30 Nov 2015 10:05:13 +0100 Subject: [PATCH 072/240] Update gem nokogiri to 1.6.7. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index b5e4b2ebb..3c1e1e598 100644 --- a/Gemfile +++ b/Gemfile @@ -68,7 +68,7 @@ gem 'active_model_serializers', '~> 0.9.3' gem 'json-stream', '~> 0.2.1' # XML Parser -gem 'nokogiri', '~> 1.6.6.2' +gem 'nokogiri', '~> 1.6.7' # Authentication gem 'devise', '~> 3.5.2' From 6f5b88a8ee26350904236f684fec9d7d3bd3bf18 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 30 Nov 2015 10:08:17 +0100 Subject: [PATCH 073/240] Update gem eye to 0.8. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3c1e1e598..3971827e6 100644 --- a/Gemfile +++ b/Gemfile @@ -180,7 +180,7 @@ end group :production do # puma is __the only exception__ for which we don't specify a version. gem 'puma' - gem 'eye', '~> 0.8.pre2' + gem 'eye', '~> 0.8' gem 'exception_notification', '~> 4.1.0' end From 8331071d0f8e0f623238c06e9abdb6c29f0a48a6 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 30 Nov 2015 10:08:40 +0100 Subject: [PATCH 074/240] Update gem faker to 1.6.1. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3971827e6..389865fe8 100644 --- a/Gemfile +++ b/Gemfile @@ -108,7 +108,7 @@ gem 'elasticsearch-extensions', '~> 0.0.15' gem 'ruby-graphviz', "~> 1.2.2" # Fake-inputs for tests and seeds -gem "faker", "~> 1.5.0" +gem "faker", "~> 1.6.1" # Git gem 'rugged', '~> 0.23.2' From dead98dbd1ce2ca4c395bd75d11986aa043e5cd3 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 30 Nov 2015 10:17:50 +0100 Subject: [PATCH 075/240] Bundle update. --- Gemfile.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index bd964f8fc..5d14d45e4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -228,7 +228,7 @@ GEM actionmailer (>= 3.0.4) activesupport (>= 3.0.4) execjs (2.6.0) - eye (0.8.pre2) + eye (0.8) celluloid (~> 0.17.2) celluloid-io (~> 0.17.0) sigar (~> 0.7.3) @@ -241,7 +241,7 @@ GEM factory_girl_rails (4.5.0) factory_girl (~> 4.5.0) railties (>= 3.0.0) - faker (1.5.0) + faker (1.6.1) i18n (~> 0.5) faraday (0.9.2) multipart-post (>= 1.2, < 3) @@ -336,7 +336,7 @@ GEM metaclass (0.0.4) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.6.2) + mini_portile2 (2.0.0) mocha (1.1.0) metaclass (~> 0.0.1) momentjs-rails (2.10.6) @@ -349,8 +349,8 @@ GEM net-ssh (3.0.1) netrc (0.11.0) nio4r (1.1.1) - nokogiri (1.6.6.2) - mini_portile (~> 0.6.0) + nokogiri (1.6.7) + mini_portile2 (~> 2.0.0.rc2) options (2.3.2) orm_adapter (0.5.0) pg (0.18.4) @@ -540,7 +540,7 @@ GEM treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.45) + tzinfo (0.3.46) uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) @@ -595,9 +595,9 @@ DEPENDENCIES elasticsearch-model (~> 0.1.4) elasticsearch-rails (~> 0.1.4) exception_notification (~> 4.1.0) - eye (~> 0.8.pre2) + eye (~> 0.8) factory_girl_rails (~> 4.5.0) - faker (~> 1.5.0) + faker (~> 1.6.1) font-awesome-sass (~> 4.4.0) foreigner (~> 1.7.2) haml-rails (~> 0.4) @@ -617,7 +617,7 @@ DEPENDENCIES launchy (~> 2.4.3) mocha (~> 1.1.0) momentjs-rails (~> 2.10.2) - nokogiri (~> 1.6.6.2) + nokogiri (~> 1.6.7) ontology-united! pg (~> 0.18.1) poltergeist (~> 1.8.0) From daf3e28fff47a65b53d6fb65155301763b9f166e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 9 Sep 2014 15:54:42 +0200 Subject: [PATCH 076/240] add backup script --- script/backup | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100755 script/backup diff --git a/script/backup b/script/backup new file mode 100755 index 000000000..62c6160e0 --- /dev/null +++ b/script/backup @@ -0,0 +1,156 @@ +#!/usr/bin/env ruby + +require 'fileutils' +require 'pathname' + +module Super + class Backup + MAINTENANCE_FILE = 'maintenance.txt' + + SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql' + REPOSITORY_FILE = 'ontohub_repositories.tar.gz' + + DATA_DIRS = %w(data/repositories data/git_daemon) + + attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir + attr_reader :dry_run, :verbose, :sql_dump_without_su + + def initialize(db_name, data_root, backup_root, + verbose: false, dry_run: true, sql_dump_without_su: false) + @db_name = db_name + @data_root = Pathname.new(data_root) + @backup_root = Pathname.new(backup_root) + + @dry_run = dry_run + @verbose = verbose + @sql_dump_without_su = sql_dump_without_su + end + + def create + enable_maintenance_mode + initialize_backup + create_sql_dump + create_repository_archive + # We needed to create the directory for the script to continue later on. + Dir.rmdir(backup_instance_dir) if dry_run + disable_maintenance_mode + puts "created backup in #{backup_instance_dir}" + end + + def restore(backup_name) + enable_maintenance_mode + initialize_restore(backup_name) + restore_sql_dump + restore_repository_archive + disable_maintenance_mode + puts "restored backup from #{backup_instance_dir}" + end + + protected + + def new_backup_name + Time.now.strftime("%Y-%m-%d_%H-%M-%S") + end + + def initialize_backup + @backup_instance_dir = backup_root.join(new_backup_name) + puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose + # Create directory even in dry run to let the script continue. + FileUtils.mkdir_p(backup_instance_dir) + end + + def create_sql_dump + Dir.chdir(backup_instance_dir) do + if sql_dump_without_su + exec "pg_dump -U postgres -Fc #{db_name} > #{SQL_DUMP_FILE}" + else + exec "sudo -u postgres pg_dump -Fc #{db_name} > #{SQL_DUMP_FILE}" + end + end + end + + def create_repository_archive + Dir.chdir(data_root.join('..')) do + archive_file = backup_instance_dir.join(REPOSITORY_FILE) + exec(["tar -czvf #{archive_file}", *DATA_DIRS].join(' ')) + end + end + + def initialize_restore(backup_name) + @backup_instance_dir = backup_root.join(backup_name) + unless Dir.exists?(backup_instance_dir) + puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}." + exit + end + end + + def restore_sql_dump + Dir.chdir(backup_instance_dir) do + if sql_dump_without_su + exec "pg_restore -c -U postgres -d #{db_name} #{SQL_DUMP_FILE}" + else + exec "sudo -u postgres pg_restore -c -d #{db_name} #{SQL_DUMP_FILE}" + end + end + end + + def restore_repository_archive + Dir.chdir(data_root.join('..')) do + puts "FileUtils.rm_r #{DATA_DIRS}" if verbose + FileUtils.rm_r DATA_DIRS unless dry_run + archive_file = backup_instance_dir.join(REPOSITORY_FILE) + exec(["tar -xzvf #{archive_file}", *DATA_DIRS].join(' ')) + end + end + + def enable_maintenance_mode + puts "FileUtils.touch #{data_root.join(MAINTENANCE_FILE)}" if verbose + FileUtils.touch data_root.join(MAINTENANCE_FILE) unless dry_run + end + + def disable_maintenance_mode + puts "FileUtils.rm #{data_root.join(MAINTENANCE_FILE)}" if verbose + FileUtils.rm data_root.join(MAINTENANCE_FILE) unless dry_run + end + + def exec(command) + puts "[executing next command in #{Dir.getwd}]" if verbose + puts command if verbose + system(command) unless dry_run + end + end +end + +def data_root(rails_root) + File.realpath(rails_root.join('data')) +end + +def on_development_system?(rails_root) + !File.symlink?(rails_root.join('data')) +end + +# We assume, this script runs in "RAILS_ROOT/script/". +RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') +DATABASE = on_development_system?(RAILS_ROOT) ? 'ontohub_development' : 'ontohub' +BACKUP_ROOT = '/tmp/ontohub_backup' + +backup = Super::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, + sql_dump_without_su: on_development_system?(RAILS_ROOT), + dry_run: false, verbose: true) + +case ARGV.first +when 'create' + backup.create +when 'restore' + if ARGV.length == 1 + puts 'To restore a backup, you need to specify one with the arguments' + puts '"restore backup_name"' + exit + end + backup_name = ARGV[1] + backup.restore(backup_name) +else + puts 'unknown or missing parameter' + puts 'use parameter "create" or "restore "' + exit +end From b1fe9054ad7c7192fe4c474363247dad15963e99 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 16 Sep 2014 16:29:30 +0200 Subject: [PATCH 077/240] rename module to Backup --- script/backup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index 62c6160e0..9c0caddbc 100755 --- a/script/backup +++ b/script/backup @@ -3,7 +3,7 @@ require 'fileutils' require 'pathname' -module Super +module Backup class Backup MAINTENANCE_FILE = 'maintenance.txt' @@ -134,7 +134,7 @@ RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') DATABASE = on_development_system?(RAILS_ROOT) ? 'ontohub_development' : 'ontohub' BACKUP_ROOT = '/tmp/ontohub_backup' -backup = Super::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, +backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, sql_dump_without_su: on_development_system?(RAILS_ROOT), dry_run: false, verbose: true) From 486df98bbf3348cfb96e93c3e499d12435880bb5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 16 Sep 2014 16:38:21 +0200 Subject: [PATCH 078/240] add helper method for maintenance file path --- script/backup | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/script/backup b/script/backup index 9c0caddbc..b07616509 100755 --- a/script/backup +++ b/script/backup @@ -104,13 +104,13 @@ module Backup end def enable_maintenance_mode - puts "FileUtils.touch #{data_root.join(MAINTENANCE_FILE)}" if verbose - FileUtils.touch data_root.join(MAINTENANCE_FILE) unless dry_run + puts "FileUtils.touch #{maintenance_file}" if verbose + FileUtils.touch maintenance_file unless dry_run end def disable_maintenance_mode - puts "FileUtils.rm #{data_root.join(MAINTENANCE_FILE)}" if verbose - FileUtils.rm data_root.join(MAINTENANCE_FILE) unless dry_run + puts "FileUtils.rm #{maintenance_file}" if verbose + FileUtils.rm maintenance_file unless dry_run end def exec(command) @@ -118,6 +118,10 @@ module Backup puts command if verbose system(command) unless dry_run end + + def maintenance_file + data_root.join(MAINTENANCE_FILE) + end end end From 0b99be5ced371cca00283694e1bd53a8ac0d7b5d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 16 Sep 2014 16:50:48 +0200 Subject: [PATCH 079/240] put backups into ontohub home --- script/backup | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index b07616509..7926c6a4e 100755 --- a/script/backup +++ b/script/backup @@ -135,8 +135,9 @@ end # We assume, this script runs in "RAILS_ROOT/script/". RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') + DATABASE = on_development_system?(RAILS_ROOT) ? 'ontohub_development' : 'ontohub' -BACKUP_ROOT = '/tmp/ontohub_backup' +BACKUP_ROOT = File.realpath('/home/ontohub/ontohub_data_backup') backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, sql_dump_without_su: on_development_system?(RAILS_ROOT), From 5efadb4662f2a63d5f5f1a5b303ab7c3371069a8 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 6 Oct 2014 14:10:02 +0200 Subject: [PATCH 080/240] Add pruning of backups. --- script/backup | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 7926c6a4e..a15759688 100755 --- a/script/backup +++ b/script/backup @@ -5,6 +5,11 @@ require 'pathname' module Backup class Backup + # Amount of backups that have to be there at least + BACKUPS_COUNT = 3 + # Backups are valid for 7 days + BACKUPS_VALIDITY_TIME = 7 * 60 * 60 * 24 + MAINTENANCE_FILE = 'maintenance.txt' SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql' @@ -35,6 +40,7 @@ module Backup Dir.rmdir(backup_instance_dir) if dry_run disable_maintenance_mode puts "created backup in #{backup_instance_dir}" + self.class.prune(backup_root) end def restore(backup_name) @@ -46,6 +52,21 @@ module Backup puts "restored backup from #{backup_instance_dir}" end + def self.prune(backup_root) + if !Dir.exists?(backup_root) + puts "Nothing to prune: There is no backup directory." + return + end + now = Time.now + backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir| + backup = backup_root.join(dir) + if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME + puts "removing old backup: #{dir}" + FileUtils.rm_r(backup) + end + end + end + protected def new_backup_name @@ -122,6 +143,10 @@ module Backup def maintenance_file data_root.join(MAINTENANCE_FILE) end + + def self.backup_dirs_allowed_to_delete(entries) + entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)] + end end end @@ -154,8 +179,10 @@ when 'restore' end backup_name = ARGV[1] backup.restore(backup_name) +when 'prune' + Backup::Backup.prune(BACKUP_ROOT) else puts 'unknown or missing parameter' - puts 'use parameter "create" or "restore "' + puts 'use parameter "create" or "restore " or "prune"' exit end From df4ff7ec6be98e7ae5830731becc1a3d55105378 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 6 Oct 2014 15:02:10 +0200 Subject: [PATCH 081/240] Set different backup directory on develop system. --- script/backup | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index a15759688..3055034fa 100755 --- a/script/backup +++ b/script/backup @@ -161,8 +161,16 @@ end # We assume, this script runs in "RAILS_ROOT/script/". RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') -DATABASE = on_development_system?(RAILS_ROOT) ? 'ontohub_development' : 'ontohub' -BACKUP_ROOT = File.realpath('/home/ontohub/ontohub_data_backup') +DATABASE = if on_development_system?(RAILS_ROOT) + 'ontohub_development' + else + 'ontohub' + end +BACKUP_ROOT = if on_development_system?(RAILS_ROOT) + RAILS_ROOT.join('tmp', 'backup') + else + File.realpath('/home/ontohub/ontohub_data_backup') + end backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, sql_dump_without_su: on_development_system?(RAILS_ROOT), From 366ce8d807067a97613cb23d49105d8a093c5015 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 6 Oct 2014 15:38:00 +0200 Subject: [PATCH 082/240] Don't sudo -u postgres call on pg_{dump,restore}. --- script/backup | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/script/backup b/script/backup index 3055034fa..5156d50d8 100755 --- a/script/backup +++ b/script/backup @@ -18,17 +18,17 @@ module Backup DATA_DIRS = %w(data/repositories data/git_daemon) attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir - attr_reader :dry_run, :verbose, :sql_dump_without_su + attr_reader :dry_run, :verbose, :sql_dump_as_postgres_user def initialize(db_name, data_root, backup_root, - verbose: false, dry_run: true, sql_dump_without_su: false) + verbose: false, dry_run: true, sql_dump_as_postgres_user: false) @db_name = db_name @data_root = Pathname.new(data_root) @backup_root = Pathname.new(backup_root) @dry_run = dry_run @verbose = verbose - @sql_dump_without_su = sql_dump_without_su + @sql_dump_as_postgres_user = sql_dump_as_postgres_user end def create @@ -82,10 +82,10 @@ module Backup def create_sql_dump Dir.chdir(backup_instance_dir) do - if sql_dump_without_su + if sql_dump_as_postgres_user exec "pg_dump -U postgres -Fc #{db_name} > #{SQL_DUMP_FILE}" else - exec "sudo -u postgres pg_dump -Fc #{db_name} > #{SQL_DUMP_FILE}" + exec "pg_dump -Fc #{db_name} > #{SQL_DUMP_FILE}" end end end @@ -107,10 +107,10 @@ module Backup def restore_sql_dump Dir.chdir(backup_instance_dir) do - if sql_dump_without_su + if sql_dump_as_postgres_user exec "pg_restore -c -U postgres -d #{db_name} #{SQL_DUMP_FILE}" else - exec "sudo -u postgres pg_restore -c -d #{db_name} #{SQL_DUMP_FILE}" + exec "pg_restore -c -d #{db_name} #{SQL_DUMP_FILE}" end end end @@ -173,7 +173,7 @@ BACKUP_ROOT = if on_development_system?(RAILS_ROOT) end backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, - sql_dump_without_su: on_development_system?(RAILS_ROOT), + sql_dump_as_postgres_user: on_development_system?(RAILS_ROOT), dry_run: false, verbose: true) case ARGV.first From 6d055d16c7620b7804b6a46cb481d00b3dbb5007 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 6 Oct 2014 15:41:10 +0200 Subject: [PATCH 083/240] Simplify pg_{dump,restore} calls. --- script/backup | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/script/backup b/script/backup index 5156d50d8..ebf5b6c25 100755 --- a/script/backup +++ b/script/backup @@ -82,11 +82,7 @@ module Backup def create_sql_dump Dir.chdir(backup_instance_dir) do - if sql_dump_as_postgres_user - exec "pg_dump -U postgres -Fc #{db_name} > #{SQL_DUMP_FILE}" - else - exec "pg_dump -Fc #{db_name} > #{SQL_DUMP_FILE}" - end + exec "pg_dump#{pg_user_switch} -Fc #{db_name} > #{SQL_DUMP_FILE}" end end @@ -107,11 +103,7 @@ module Backup def restore_sql_dump Dir.chdir(backup_instance_dir) do - if sql_dump_as_postgres_user - exec "pg_restore -c -U postgres -d #{db_name} #{SQL_DUMP_FILE}" - else - exec "pg_restore -c -d #{db_name} #{SQL_DUMP_FILE}" - end + exec "pg_restore -c#{pg_user_switch} -d #{db_name} #{SQL_DUMP_FILE}" end end @@ -144,6 +136,10 @@ module Backup data_root.join(MAINTENANCE_FILE) end + def pg_user_switch + sql_dump_as_postgres_user ? ' -U postgres' : '' + end + def self.backup_dirs_allowed_to_delete(entries) entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)] end From dfff4029890084ba790248aca3a952547a78c5cb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 6 Oct 2014 16:02:04 +0200 Subject: [PATCH 084/240] chmod -R (allow deletion) before rm -r --- script/backup | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/script/backup b/script/backup index ebf5b6c25..8a1390961 100755 --- a/script/backup +++ b/script/backup @@ -109,8 +109,12 @@ module Backup def restore_repository_archive Dir.chdir(data_root.join('..')) do + puts "FileUtils.chmod_R 0770, #{DATA_DIRS}" if verbose + FileUtils.chmod_R 0770, DATA_DIRS unless dry_run + puts "FileUtils.rm_r #{DATA_DIRS}" if verbose FileUtils.rm_r DATA_DIRS unless dry_run + archive_file = backup_instance_dir.join(REPOSITORY_FILE) exec(["tar -xzvf #{archive_file}", *DATA_DIRS].join(' ')) end From b51057b860560bf3ee454c03a121af3d5d34f482 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 10:49:42 +0200 Subject: [PATCH 085/240] Backup data dirs on restore, set correct permissions Backup of data dirs: When restoring a backup, we need to remove the currently active data directories in order to restore the ones from the backup archive. This is done by moving them to a temp directory, then extracting the archive and deleting the temp directory. This way, if something goes wrong during extraction, one can still recover the pre-restore repositories. Since they are git repositories and we don't allow force-pushes, they may still be of use. Permissions: To reset the permissions on the extracted repositories, super user privileges are needed. This is accomplished by first trying to get those privileges for the extraction. If the user refuses to enter the password (wrong one three times or Ctrl-C), the extraction will be done as the normal user. This way, the repository archive is still extracted, but all the files belong to the restorign user. --- script/backup | 76 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/script/backup b/script/backup index 8a1390961..8aca2f91e 100755 --- a/script/backup +++ b/script/backup @@ -1,7 +1,10 @@ #!/usr/bin/env ruby +require 'tmpdir.rb' require 'fileutils' require 'pathname' +require 'pry' +require 'pry-byebug' module Backup class Backup @@ -109,15 +112,58 @@ module Backup def restore_repository_archive Dir.chdir(data_root.join('..')) do - puts "FileUtils.chmod_R 0770, #{DATA_DIRS}" if verbose - FileUtils.chmod_R 0770, DATA_DIRS unless dry_run + tmpdir = Dir.mktmpdir + begin + move_data_dirs_to_tmpdir(tmpdir) + extract_archive + remove_tmpdir(tmpdir) + rescue => e + puts <<-MSG +An error occured while restoring the repositories: +#{e.message} +You can find the pre-restore repositories at #{tmpdir} +Do something about it. + MSG + raise e + end + end + end - puts "FileUtils.rm_r #{DATA_DIRS}" if verbose - FileUtils.rm_r DATA_DIRS unless dry_run + def move_data_dirs_to_tmpdir(tmpdir) + puts "FileUtils.mv(#{DATA_DIRS}, #{tmpdir})" if verbose + FileUtils.mv(DATA_DIRS, tmpdir) unless dry_run + rescue Errno::EACCES + puts <<-MSG +As the current user I have no access to move the repository data +directories #{DATA_DIRS.join(' ')} to a temporary directory #{tmpdir}. +This is used as a backup for the case of an error while restoring. +To continue, I try the command again using sudo. + MSG + exec('sudo', 'mv', *DATA_DIRS.map(&:to_s), tmpdir) + end - archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec(["tar -xzvf #{archive_file}", *DATA_DIRS].join(' ')) - end + def extract_archive + archive_file = backup_instance_dir.join(REPOSITORY_FILE) + puts <<-MSG +Super user privileges are needed to reset the file permissions as +they were before the backup. If you refuse to enter the password +(Crtl-C) or enter a wrong password, only the permissions will not be +restored and all restored files will belong to the current user/group. + MSG + try_as_sudo_with_fallback('tar', '-xzvf', archive_file.to_s, + *DATA_DIRS.map(&:to_s)) + end + + def remove_tmpdir(tmpdir) + puts "FileUtils.remove_entry(#{tmpdir})" if verbose + FileUtils.remove_entry(tmpdir) # even do this in dry run + rescue Errno::EACCES + puts <<-MSG +As the current user I have no access to remove the temporary +directory #{tmpdir}. +To continue, I try the command again using sudo. + MSG + exec('sudo', 'rm', '-r', tmpdir) end def enable_maintenance_mode @@ -136,6 +182,22 @@ module Backup system(command) unless dry_run end + def try_as_sudo_with_fallback(command) + unless exec("sudo #{command}") + # Wrong sudo password + sudo_not_given_fallback(command) + end + rescue Exception => e + # Ctrl-C when asked for password + raise e unless e.is_a?(Interrupt) + sudo_not_given_fallback(command) + end + + def sudo_not_given_fallback(command) + puts 'Super user privileges not granted. Trying as normal user.' + exec(command) + end + def maintenance_file data_root.join(MAINTENANCE_FILE) end From 28001d576e67ba46ed481c5695f1e0827ff26007 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 11:42:26 +0200 Subject: [PATCH 086/240] Use open3 to send system calls. --- script/backup | 53 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/script/backup b/script/backup index 8aca2f91e..ad499dbf3 100755 --- a/script/backup +++ b/script/backup @@ -3,6 +3,7 @@ require 'tmpdir.rb' require 'fileutils' require 'pathname' +require 'open3' require 'pry' require 'pry-byebug' @@ -85,14 +86,15 @@ module Backup def create_sql_dump Dir.chdir(backup_instance_dir) do - exec "pg_dump#{pg_user_switch} -Fc #{db_name} > #{SQL_DUMP_FILE}" + exec('pg_dump', pg_user_switch, '-Fc', db_name, + file_dest: SQL_DUMP_FILE) end end def create_repository_archive Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec(["tar -czvf #{archive_file}", *DATA_DIRS].join(' ')) + exec('tar', '-czvf', archive_file.to_s, *DATA_DIRS.map(&:to_s)) end end @@ -106,7 +108,7 @@ module Backup def restore_sql_dump Dir.chdir(backup_instance_dir) do - exec "pg_restore -c#{pg_user_switch} -d #{db_name} #{SQL_DUMP_FILE}" + exec('pg_restore', '-c', pg_user_switch, '-d', db_name, SQL_DUMP_FILE) end end @@ -176,26 +178,26 @@ To continue, I try the command again using sudo. FileUtils.rm maintenance_file unless dry_run end - def exec(command) + def exec(*args, file_dest: nil) puts "[executing next command in #{Dir.getwd}]" if verbose - puts command if verbose - system(command) unless dry_run + out = args.join(' ') + out << " > #{file_dest}" if file_dest + puts out if verbose + Subprocess.run(*args, file_dest: file_dest) unless dry_run end - def try_as_sudo_with_fallback(command) - unless exec("sudo #{command}") - # Wrong sudo password - sudo_not_given_fallback(command) + def try_as_sudo_with_fallback(*args) + unless exec('sudo', *args) + sudo_not_given_fallback(*args) # Wrong sudo password end rescue Exception => e - # Ctrl-C when asked for password - raise e unless e.is_a?(Interrupt) - sudo_not_given_fallback(command) + raise e unless e.is_a?(Interrupt) # Ctrl-C when asked for password + sudo_not_given_fallback(*args) end - def sudo_not_given_fallback(command) + def sudo_not_given_fallback(*args) puts 'Super user privileges not granted. Trying as normal user.' - exec(command) + exec(*args) end def maintenance_file @@ -210,6 +212,27 @@ To continue, I try the command again using sudo. entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)] end end + + class Subprocess + def self.run(*args, file_dest: nil) + stdin, stdout, stderr, wait_thr, io_dest = run_streaming(*args, + file_dest: file_dest) + exit_code = wait_thr.value # wait for the process to finish + io_dest.close if io_dest + + [stdout.read, stderr.read, exit_code] + end + + def self.run_streaming(*args, file_dest: nil) + stdin, stdout, stderr, wait_thr = Open3.popen3(*args) + io_dest = nil + if file_dest + io_dest = File.open(file_dest, 'w') + IO.copy_stream(stdout, io_dest) + end + [stdin, stdout, stderr, wait_thr, io_dest] + end + end end def data_root(rails_root) From 923d69139038e74c0936e826bbfdc8717fbbc7b3 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 11:46:32 +0200 Subject: [PATCH 087/240] print errors on stderr --- script/backup | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/backup b/script/backup index ad499dbf3..160521504 100755 --- a/script/backup +++ b/script/backup @@ -58,7 +58,7 @@ module Backup def self.prune(backup_root) if !Dir.exists?(backup_root) - puts "Nothing to prune: There is no backup directory." + $stderr.puts "Nothing to prune: There is no backup directory." return end now = Time.now @@ -101,7 +101,7 @@ module Backup def initialize_restore(backup_name) @backup_instance_dir = backup_root.join(backup_name) unless Dir.exists?(backup_instance_dir) - puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}." + $stderr.puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}." exit end end @@ -266,8 +266,8 @@ when 'create' backup.create when 'restore' if ARGV.length == 1 - puts 'To restore a backup, you need to specify one with the arguments' - puts '"restore backup_name"' + $stderr.puts 'To restore a backup, you need to specify one with the arguments' + $stderr.puts '"restore backup_name"' exit end backup_name = ARGV[1] @@ -275,7 +275,7 @@ when 'restore' when 'prune' Backup::Backup.prune(BACKUP_ROOT) else - puts 'unknown or missing parameter' - puts 'use parameter "create" or "restore " or "prune"' + $stderr.puts 'unknown or missing parameter' + $stderr.puts 'use parameter "create" or "restore " or "prune"' exit end From 284432981d641cf3d679841f75acbcf039d83062 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 11:59:34 +0200 Subject: [PATCH 088/240] Use verbose flag for tar command as well. --- script/backup | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index 160521504..7c01cd6c5 100755 --- a/script/backup +++ b/script/backup @@ -94,7 +94,8 @@ module Backup def create_repository_archive Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec('tar', '-czvf', archive_file.to_s, *DATA_DIRS.map(&:to_s)) + exec('tar', verbose ? '-v' : '', '-czf', archive_file.to_s, + *DATA_DIRS.map(&:to_s)) end end @@ -147,13 +148,14 @@ To continue, I try the command again using sudo. def extract_archive archive_file = backup_instance_dir.join(REPOSITORY_FILE) puts <<-MSG + Super user privileges are needed to reset the file permissions as they were before the backup. If you refuse to enter the password (Crtl-C) or enter a wrong password, only the permissions will not be restored and all restored files will belong to the current user/group. MSG - try_as_sudo_with_fallback('tar', '-xzvf', archive_file.to_s, - *DATA_DIRS.map(&:to_s)) + try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xzf', + archive_file.to_s, *DATA_DIRS.map(&:to_s)) end def remove_tmpdir(tmpdir) From 2dbc668d1e44c95db1857d3968bcde7517852bea Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 12:02:24 +0200 Subject: [PATCH 089/240] Don't rescue from error when moving data dirs. --- script/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 7c01cd6c5..49aed2723 100755 --- a/script/backup +++ b/script/backup @@ -116,8 +116,8 @@ module Backup def restore_repository_archive Dir.chdir(data_root.join('..')) do tmpdir = Dir.mktmpdir + move_data_dirs_to_tmpdir(tmpdir) begin - move_data_dirs_to_tmpdir(tmpdir) extract_archive remove_tmpdir(tmpdir) rescue => e From 4ec9d8b62c3c1a001548eb0883b6f81e00c391a0 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 12:03:01 +0200 Subject: [PATCH 090/240] Print more information on the whole process. --- script/backup | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index 49aed2723..89ba45c24 100755 --- a/script/backup +++ b/script/backup @@ -36,6 +36,7 @@ module Backup end def create + puts 'Creating backup...' enable_maintenance_mode initialize_backup create_sql_dump @@ -43,7 +44,7 @@ module Backup # We needed to create the directory for the script to continue later on. Dir.rmdir(backup_instance_dir) if dry_run disable_maintenance_mode - puts "created backup in #{backup_instance_dir}" + puts "Created backup in #{backup_instance_dir}" self.class.prune(backup_root) end @@ -53,7 +54,7 @@ module Backup restore_sql_dump restore_repository_archive disable_maintenance_mode - puts "restored backup from #{backup_instance_dir}" + puts "Restored backup from #{backup_instance_dir}" end def self.prune(backup_root) @@ -85,6 +86,7 @@ module Backup end def create_sql_dump + puts 'Creating SQL dump...' Dir.chdir(backup_instance_dir) do exec('pg_dump', pg_user_switch, '-Fc', db_name, file_dest: SQL_DUMP_FILE) @@ -92,6 +94,7 @@ module Backup end def create_repository_archive + puts 'Creating repository archive...' Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) exec('tar', verbose ? '-v' : '', '-czf', archive_file.to_s, @@ -108,12 +111,14 @@ module Backup end def restore_sql_dump + 'Restoring SQL dump...' Dir.chdir(backup_instance_dir) do exec('pg_restore', '-c', pg_user_switch, '-d', db_name, SQL_DUMP_FILE) end end def restore_repository_archive + puts 'Restoring repository archive...' Dir.chdir(data_root.join('..')) do tmpdir = Dir.mktmpdir move_data_dirs_to_tmpdir(tmpdir) @@ -122,6 +127,7 @@ module Backup remove_tmpdir(tmpdir) rescue => e puts <<-MSG + An error occured while restoring the repositories: #{e.message} You can find the pre-restore repositories at #{tmpdir} @@ -137,6 +143,7 @@ Do something about it. FileUtils.mv(DATA_DIRS, tmpdir) unless dry_run rescue Errno::EACCES puts <<-MSG + As the current user I have no access to move the repository data directories #{DATA_DIRS.join(' ')} to a temporary directory #{tmpdir}. This is used as a backup for the case of an error while restoring. @@ -163,6 +170,7 @@ restored and all restored files will belong to the current user/group. FileUtils.remove_entry(tmpdir) # even do this in dry run rescue Errno::EACCES puts <<-MSG + As the current user I have no access to remove the temporary directory #{tmpdir}. To continue, I try the command again using sudo. @@ -171,11 +179,13 @@ To continue, I try the command again using sudo. end def enable_maintenance_mode + puts 'Enabling maintenance mode...' puts "FileUtils.touch #{maintenance_file}" if verbose FileUtils.touch maintenance_file unless dry_run end def disable_maintenance_mode + puts 'Disabling maintenance mode...' puts "FileUtils.rm #{maintenance_file}" if verbose FileUtils.rm maintenance_file unless dry_run end From dd553f2f8b8abb774ba64a4fb9ebe3abea9f7f17 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 12:10:46 +0200 Subject: [PATCH 091/240] fix wrong sudo password issue --- script/backup | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 89ba45c24..87170b1d9 100755 --- a/script/backup +++ b/script/backup @@ -199,7 +199,8 @@ To continue, I try the command again using sudo. end def try_as_sudo_with_fallback(*args) - unless exec('sudo', *args) + _out, _err, exit_code = exec('sudo', *args) + unless exit_code.success? sudo_not_given_fallback(*args) # Wrong sudo password end rescue Exception => e From d89f470f7da0b9f8295d0ac0defff09884894b8b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 8 Oct 2014 12:18:04 +0200 Subject: [PATCH 092/240] Adjust constants for pruning. --- script/backup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index 87170b1d9..995a155be 100755 --- a/script/backup +++ b/script/backup @@ -10,9 +10,9 @@ require 'pry-byebug' module Backup class Backup # Amount of backups that have to be there at least - BACKUPS_COUNT = 3 - # Backups are valid for 7 days - BACKUPS_VALIDITY_TIME = 7 * 60 * 60 * 24 + BACKUPS_COUNT = 30 + # Backups are kept for at least 365 days + BACKUPS_VALIDITY_TIME = 365 * 60 * 60 * 24 MAINTENANCE_FILE = 'maintenance.txt' From fad858ab19d7e43677a156585c5bbb4341a87655 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 6 Dec 2014 16:55:40 +0100 Subject: [PATCH 093/240] Remove pry. --- script/backup | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/backup b/script/backup index 995a155be..45e39cf85 100755 --- a/script/backup +++ b/script/backup @@ -4,8 +4,6 @@ require 'tmpdir.rb' require 'fileutils' require 'pathname' require 'open3' -require 'pry' -require 'pry-byebug' module Backup class Backup From 4634cde5d3428bd5ab34b8212ac2f4637cdfff6f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 6 Dec 2014 16:55:59 +0100 Subject: [PATCH 094/240] Obey Hound. --- script/backup | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index 45e39cf85..b988fa02c 100755 --- a/script/backup +++ b/script/backup @@ -103,7 +103,8 @@ module Backup def initialize_restore(backup_name) @backup_instance_dir = backup_root.join(backup_name) unless Dir.exists?(backup_instance_dir) - $stderr.puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}." + $stderr.puts ( + "Error: Backup '#{backup_name}' does not exist in #{backup_root}.") exit end end @@ -277,8 +278,9 @@ when 'create' backup.create when 'restore' if ARGV.length == 1 - $stderr.puts 'To restore a backup, you need to specify one with the arguments' - $stderr.puts '"restore backup_name"' + $stderr.puts( + 'To restore a backup, you need to specify one with the arguments') + $stderr.puts('"restore backup_name"') exit end backup_name = ARGV[1] From b4ac7fd47818fbcba8d344e3de41ca62e1473b94 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 6 Dec 2014 19:59:44 +0100 Subject: [PATCH 095/240] Fix arguments for pg dump. --- script/backup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index b988fa02c..2bc465b8b 100755 --- a/script/backup +++ b/script/backup @@ -86,7 +86,7 @@ module Backup def create_sql_dump puts 'Creating SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_dump', pg_user_switch, '-Fc', db_name, + exec('pg_dump', *pg_user_switch, '-Fc', db_name, file_dest: SQL_DUMP_FILE) end end @@ -112,7 +112,7 @@ module Backup def restore_sql_dump 'Restoring SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_restore', '-c', pg_user_switch, '-d', db_name, SQL_DUMP_FILE) + exec('pg_restore', '-c', *pg_user_switch, '-d', db_name, SQL_DUMP_FILE) end end @@ -217,7 +217,7 @@ To continue, I try the command again using sudo. end def pg_user_switch - sql_dump_as_postgres_user ? ' -U postgres' : '' + sql_dump_as_postgres_user ? %w(-U postgres) : [] end def self.backup_dirs_allowed_to_delete(entries) From f0264afd33a980b6584747fc8159ee950805d9e3 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 3 Jun 2015 10:34:59 +0200 Subject: [PATCH 096/240] Add documentation to the backup script. --- script/backup | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/script/backup b/script/backup index 2bc465b8b..7f317fc79 100755 --- a/script/backup +++ b/script/backup @@ -1,5 +1,39 @@ #!/usr/bin/env ruby +# Description +# This backup script creates and restores backups of ontohub data. It includes: +# * bare git repositories (data/repositories) +# * named symlinks to git repositories (data/git_daemon) +# * the postgres database +# +# Usage +# To create a backup, run this script with the argument `create`: +# $ script/backup create +# Then a backup named with the current date and time is created in the +# backup directory (see below). +# +# To restore a backup, run this script with the argument `restore ` +# $ script/backup restore 2015-01-01_00-00 +# Then the selected backup is fully restored +# +# Backup directory +# For development machines, the backup directory is: +# /tmp/backup/ +# And for production machines, the backup directory is: +# /home/ontohub/ontohub_data_backup +# +# Super user privileges +# To create and restore, we need root privileges. Otherwise file modes are not +# preserved. This script will call `sudo` when needed and inform you about the +# reason for calling `sudo`. If you don't allow sudo, a backup will be created +# or restored anyway, but the file modes and ownership are not preserved. +# Then, you need to adjust them manually. +# +# Maintenance mode +# While backing up and restoring the data, the maintenance mode is activated. +# This way we guarantee data consistency of the backup. + + require 'tmpdir.rb' require 'fileutils' require 'pathname' From 1724834ae596f84e8237a3bba5a8af15916a8ee9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 3 Jun 2015 10:40:15 +0200 Subject: [PATCH 097/240] Improve code style. --- script/backup | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index 7f317fc79..b01532510 100755 --- a/script/backup +++ b/script/backup @@ -291,16 +291,20 @@ end # We assume, this script runs in "RAILS_ROOT/script/". RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') +BACKUP_ROOT_PRODUCTION = '/home/ontohub/ontohub_data_backup' -DATABASE = if on_development_system?(RAILS_ROOT) +DATABASE = + if on_development_system?(RAILS_ROOT) 'ontohub_development' else 'ontohub' end -BACKUP_ROOT = if on_development_system?(RAILS_ROOT) + +BACKUP_ROOT = + if on_development_system?(RAILS_ROOT) RAILS_ROOT.join('tmp', 'backup') else - File.realpath('/home/ontohub/ontohub_data_backup') + File.realpath(BACKUP_ROOT_PRODUCTION) end backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, From 6cdc461aada609d57d50ff675d29b15378717ff2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 3 Jun 2015 11:35:30 +0200 Subject: [PATCH 098/240] Fix typo. --- script/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/backup b/script/backup index b01532510..53360dbe1 100755 --- a/script/backup +++ b/script/backup @@ -191,7 +191,7 @@ To continue, I try the command again using sudo. Super user privileges are needed to reset the file permissions as they were before the backup. If you refuse to enter the password -(Crtl-C) or enter a wrong password, only the permissions will not be +(Ctl-C) or enter a wrong password, only the permissions will not be restored and all restored files will belong to the current user/group. MSG try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xzf', From 0978722f6ca27f5d5f5ed4ec8400703dfe211184 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 3 Jun 2015 11:56:27 +0200 Subject: [PATCH 099/240] Disable running the backup script as root. --- script/backup | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script/backup b/script/backup index 53360dbe1..305bbee12 100755 --- a/script/backup +++ b/script/backup @@ -7,6 +7,7 @@ # * the postgres database # # Usage +# First note: Run this as the ontohub user, *not* as root. # To create a backup, run this script with the argument `create`: # $ script/backup create # Then a backup named with the current date and time is created in the @@ -289,6 +290,13 @@ def on_development_system?(rails_root) !File.symlink?(rails_root.join('data')) end +# Don't allow this to be run as the root user. +if ENV['USER'] == 'root' + puts 'Running this script as the root user is disabled.' + puts 'Please run it as a normal user that has sudo privileges, e.g. ontohub.' + exit +end + # We assume, this script runs in "RAILS_ROOT/script/". RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') BACKUP_ROOT_PRODUCTION = '/home/ontohub/ontohub_data_backup' From 96c47a92d836bca1aca373db8664c5ec84cfc8af Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 29 Jun 2015 11:47:01 +0200 Subject: [PATCH 100/240] Use pg_dump with file parameter This renders the Subprocess class obsolete. Instead, we use system now. --- script/backup | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/script/backup b/script/backup index 305bbee12..b20da9a13 100755 --- a/script/backup +++ b/script/backup @@ -121,8 +121,7 @@ module Backup def create_sql_dump puts 'Creating SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_dump', *pg_user_switch, '-Fc', db_name, - file_dest: SQL_DUMP_FILE) + exec('pg_dump', *pg_user_switch, '-Fc', db_name, '-f', SQL_DUMP_FILE) end end @@ -224,12 +223,11 @@ To continue, I try the command again using sudo. FileUtils.rm maintenance_file unless dry_run end - def exec(*args, file_dest: nil) + def exec(*args) puts "[executing next command in #{Dir.getwd}]" if verbose out = args.join(' ') - out << " > #{file_dest}" if file_dest puts out if verbose - Subprocess.run(*args, file_dest: file_dest) unless dry_run + system(*args) unless dry_run end def try_as_sudo_with_fallback(*args) @@ -259,27 +257,6 @@ To continue, I try the command again using sudo. entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)] end end - - class Subprocess - def self.run(*args, file_dest: nil) - stdin, stdout, stderr, wait_thr, io_dest = run_streaming(*args, - file_dest: file_dest) - exit_code = wait_thr.value # wait for the process to finish - io_dest.close if io_dest - - [stdout.read, stderr.read, exit_code] - end - - def self.run_streaming(*args, file_dest: nil) - stdin, stdout, stderr, wait_thr = Open3.popen3(*args) - io_dest = nil - if file_dest - io_dest = File.open(file_dest, 'w') - IO.copy_stream(stdout, io_dest) - end - [stdin, stdout, stderr, wait_thr, io_dest] - end - end end def data_root(rails_root) From 76f98a73426869ab54073075345686d98b6a8caf Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 29 Jun 2015 15:11:17 +0200 Subject: [PATCH 101/240] Omit the parameter z of tar: It is inferred by the file type. --- script/backup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index b20da9a13..2d12c85f8 100755 --- a/script/backup +++ b/script/backup @@ -129,7 +129,7 @@ module Backup puts 'Creating repository archive...' Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec('tar', verbose ? '-v' : '', '-czf', archive_file.to_s, + exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *DATA_DIRS.map(&:to_s)) end end @@ -194,7 +194,7 @@ they were before the backup. If you refuse to enter the password (Ctl-C) or enter a wrong password, only the permissions will not be restored and all restored files will belong to the current user/group. MSG - try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xzf', + try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xf', archive_file.to_s, *DATA_DIRS.map(&:to_s)) end From bacf95fddc53ab3107f380c4c816fa8072358bd9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 29 Jun 2015 15:22:23 +0200 Subject: [PATCH 102/240] Check maintenance mode before activating it. --- script/backup | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/backup b/script/backup index 2d12c85f8..bbd50e544 100755 --- a/script/backup +++ b/script/backup @@ -213,6 +213,12 @@ To continue, I try the command again using sudo. def enable_maintenance_mode puts 'Enabling maintenance mode...' + if File.exist?(maintenance_file) + $stderr.puts 'Maintenance mode was already enabled.' + $stderr.puts "Please check the file #{maintenance_file}" + $stderr.puts 'Aborting.' + exit + end puts "FileUtils.touch #{maintenance_file}" if verbose FileUtils.touch maintenance_file unless dry_run end From abc834c020080fe44a1ea4e34278327e99e3e12e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 3 Jul 2015 14:53:04 +0200 Subject: [PATCH 103/240] Add reference to the more beautiful documentation. --- script/backup | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/backup b/script/backup index bbd50e544..0e3bdfbb1 100755 --- a/script/backup +++ b/script/backup @@ -1,5 +1,8 @@ #!/usr/bin/env ruby +# You can find more extensive documentation of this script at +# https://github.com/ontohub/ontohub/blob/staging/doc/backup_and_restore_of_ontohub_data.md + # Description # This backup script creates and restores backups of ontohub data. It includes: # * bare git repositories (data/repositories) From 1a15745c677f6b1a6dd50ac619cc310aae1b8d53 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 3 Jul 2015 14:55:19 +0200 Subject: [PATCH 104/240] Add note to database dropping. --- doc/backup_and_restore_of_ontohub_data.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/backup_and_restore_of_ontohub_data.md b/doc/backup_and_restore_of_ontohub_data.md index e96e4c1cd..0b7f5463b 100644 --- a/doc/backup_and_restore_of_ontohub_data.md +++ b/doc/backup_and_restore_of_ontohub_data.md @@ -22,9 +22,12 @@ script/backup restore 2014-12-06_20-06-37 ``` (use your own backup name - it is a folder from the above-mentioned backup directory) +Note that there must be no active connection to the database. +Otherwise restoring the SQL dump will fail. + What happens is: * It activates the maintenance mode. -* It restores the PostgreSQL dump `ontohub_sql_dump.postgresql`. +* It restores the PostgreSQL dump `ontohub_sql_dump.postgresql`. This drops and recreates the whole database. In order to drop the database, there must be no other active connection to it. * It moves the current (pre-restore) repositories to a temporary directory (which one will be printed to stdout). This is done because the admin can do something with the repositories if restore fails. * It extracts the repository archive `ontohub_repositories.tar.gz` to the Ontohub instance. This is first tried with `sudo` because the permissions/file mode are only restored if executed with super user rights. If canceled (CTRL+C) or the wrong password is entered three times, the archive is extracted without `sudo`. * It deletes the repositories which were moved to a temporary directory. From e6edf613be834fd6ab5ea816077d4263609db07e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 13:47:50 +0200 Subject: [PATCH 105/240] Adjust backup path to new deployments. --- script/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 0e3bdfbb1..41f273816 100755 --- a/script/backup +++ b/script/backup @@ -285,7 +285,7 @@ end # We assume, this script runs in "RAILS_ROOT/script/". RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') -BACKUP_ROOT_PRODUCTION = '/home/ontohub/ontohub_data_backup' +BACKUP_ROOT_PRODUCTION = '/local/home/ontohub/ontohub_data_backup' DATABASE = if on_development_system?(RAILS_ROOT) From 26e23083feb1577ee4bd102bc09ca440d20548b9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 14:26:40 +0200 Subject: [PATCH 106/240] Adjust data path to new deployments. --- script/backup | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 41f273816..e38d74644 100755 --- a/script/backup +++ b/script/backup @@ -269,7 +269,11 @@ To continue, I try the command again using sudo. end def data_root(rails_root) - File.realpath(rails_root.join('data')) + if on_development_system?(rails_root) + File.realpath(rails_root.join('data')) + else + ENV['DATA_ROOT'] ||'/data/git' + end end def on_development_system?(rails_root) From 2da9df454152009d7a5e0d83d519aa2bc9844233 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 14:27:06 +0200 Subject: [PATCH 107/240] Add possibility to specify the data path via the environment. --- script/backup | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index e38d74644..f19be0eec 100755 --- a/script/backup +++ b/script/backup @@ -277,7 +277,8 @@ def data_root(rails_root) end def on_development_system?(rails_root) - !File.symlink?(rails_root.join('data')) + data_path = rails_root.join('data') + File.exist?(data_path) && !File.symlink?(data_path) end # Don't allow this to be run as the root user. From a8028fd2789e323040de08827a0fe1f7d36fde2b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 14:39:38 +0200 Subject: [PATCH 108/240] Detect and use basename of data_root. --- script/backup | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/script/backup b/script/backup index f19be0eec..ddfc4c043 100755 --- a/script/backup +++ b/script/backup @@ -55,7 +55,7 @@ module Backup SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql' REPOSITORY_FILE = 'ontohub_repositories.tar.gz' - DATA_DIRS = %w(data/repositories data/git_daemon) + DATA_DIRS = %w(repositories git_daemon) attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir attr_reader :dry_run, :verbose, :sql_dump_as_postgres_user @@ -63,8 +63,10 @@ module Backup def initialize(db_name, data_root, backup_root, verbose: false, dry_run: true, sql_dump_as_postgres_user: false) @db_name = db_name - @data_root = Pathname.new(data_root) @backup_root = Pathname.new(backup_root) + @data_root = Pathname.new(data_root) + @data_root_basename = @data_root.basename.to_s + @data_dirs = DATA_DIRS.map { |dir| File.join(@data_root_basename, dir) } @dry_run = dry_run @verbose = verbose @@ -132,8 +134,7 @@ module Backup puts 'Creating repository archive...' Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, - *DATA_DIRS.map(&:to_s)) + exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *@data_dirs) end end @@ -175,17 +176,17 @@ Do something about it. end def move_data_dirs_to_tmpdir(tmpdir) - puts "FileUtils.mv(#{DATA_DIRS}, #{tmpdir})" if verbose - FileUtils.mv(DATA_DIRS, tmpdir) unless dry_run + puts "FileUtils.mv(#{@data_dirs}, #{tmpdir})" if verbose + FileUtils.mv(@data_dirs, tmpdir) unless dry_run rescue Errno::EACCES puts <<-MSG As the current user I have no access to move the repository data -directories #{DATA_DIRS.join(' ')} to a temporary directory #{tmpdir}. +directories #{@data_dirs.join(' ')} to a temporary directory #{tmpdir}. This is used as a backup for the case of an error while restoring. To continue, I try the command again using sudo. MSG - exec('sudo', 'mv', *DATA_DIRS.map(&:to_s), tmpdir) + exec('sudo', 'mv', *@data_dirs, tmpdir) end def extract_archive @@ -198,7 +199,7 @@ they were before the backup. If you refuse to enter the password restored and all restored files will belong to the current user/group. MSG try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xf', - archive_file.to_s, *DATA_DIRS.map(&:to_s)) + archive_file.to_s, *@data_dirs) end def remove_tmpdir(tmpdir) From 67daeb8066e4460cb820db60c45138dd48309bb9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 14:54:09 +0200 Subject: [PATCH 109/240] Use psql ontohub user instead of postgres. --- script/backup | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/backup b/script/backup index ddfc4c043..fb92a1fde 100755 --- a/script/backup +++ b/script/backup @@ -58,10 +58,10 @@ module Backup DATA_DIRS = %w(repositories git_daemon) attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir - attr_reader :dry_run, :verbose, :sql_dump_as_postgres_user + attr_reader :dry_run, :verbose, :sql_dump_as_db_user def initialize(db_name, data_root, backup_root, - verbose: false, dry_run: true, sql_dump_as_postgres_user: false) + verbose: false, dry_run: true, sql_dump_as_db_user: false) @db_name = db_name @backup_root = Pathname.new(backup_root) @data_root = Pathname.new(data_root) @@ -70,7 +70,7 @@ module Backup @dry_run = dry_run @verbose = verbose - @sql_dump_as_postgres_user = sql_dump_as_postgres_user + @sql_dump_as_db_user = sql_dump_as_db_user end def create @@ -260,7 +260,7 @@ To continue, I try the command again using sudo. end def pg_user_switch - sql_dump_as_postgres_user ? %w(-U postgres) : [] + sql_dump_as_db_user ? %w(-U ontohub) : [] end def self.backup_dirs_allowed_to_delete(entries) @@ -308,7 +308,7 @@ BACKUP_ROOT = end backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, - sql_dump_as_postgres_user: on_development_system?(RAILS_ROOT), + sql_dump_as_db_user: true, dry_run: false, verbose: true) case ARGV.first From f9e467b8fe6fef705eec2989b20e92eaa9d917e1 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 15:04:55 +0200 Subject: [PATCH 110/240] Only restore data we own. --- script/backup | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index fb92a1fde..cec31ced5 100755 --- a/script/backup +++ b/script/backup @@ -150,7 +150,10 @@ module Backup def restore_sql_dump 'Restoring SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_restore', '-c', *pg_user_switch, '-d', db_name, SQL_DUMP_FILE) + exec('pg_restore', '-n', 'public', + '-c', *pg_user_switch, + '-d', db_name, + SQL_DUMP_FILE) end end From 102ec428bcf83c38bdf85922bcd330653eb4c8dc Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 20 Jul 2015 15:27:54 +0200 Subject: [PATCH 111/240] Drop last leftover of open3 in favor of system(). --- script/backup | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/backup b/script/backup index cec31ced5..618e81e16 100755 --- a/script/backup +++ b/script/backup @@ -244,8 +244,7 @@ To continue, I try the command again using sudo. end def try_as_sudo_with_fallback(*args) - _out, _err, exit_code = exec('sudo', *args) - unless exit_code.success? + unless exec('sudo', *args) sudo_not_given_fallback(*args) # Wrong sudo password end rescue Exception => e From 9b2f473c12f708c5c9aa86d8763aa52b61eb0832 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 23 Jul 2015 11:41:56 +0200 Subject: [PATCH 112/240] Add note on file permissions to restore docs. --- doc/backup_and_restore_of_ontohub_data.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/backup_and_restore_of_ontohub_data.md b/doc/backup_and_restore_of_ontohub_data.md index 0b7f5463b..e93710bdd 100644 --- a/doc/backup_and_restore_of_ontohub_data.md +++ b/doc/backup_and_restore_of_ontohub_data.md @@ -29,6 +29,8 @@ What happens is: * It activates the maintenance mode. * It restores the PostgreSQL dump `ontohub_sql_dump.postgresql`. This drops and recreates the whole database. In order to drop the database, there must be no other active connection to it. * It moves the current (pre-restore) repositories to a temporary directory (which one will be printed to stdout). This is done because the admin can do something with the repositories if restore fails. -* It extracts the repository archive `ontohub_repositories.tar.gz` to the Ontohub instance. This is first tried with `sudo` because the permissions/file mode are only restored if executed with super user rights. If canceled (CTRL+C) or the wrong password is entered three times, the archive is extracted without `sudo`. +* It extracts the repository archive `ontohub_repositories.tar.gz` to the Ontohub instance. This is first tried with `sudo` because the permissions/file mode are only restored if executed with super user rights. If cancelled (CTRL+C) or the wrong password is entered three times, the archive is extracted without `sudo`. +The use of `sudo` is required to restore file permissions of the git repositories. +If cancelled, the file permissions need to be set manually with `chown`. * It deletes the repositories which were moved to a temporary directory. * It deactivates the maintenance mode. From 57c81a32dff6182f040b4f852892144641a62fe5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 19 Aug 2015 08:14:17 +0200 Subject: [PATCH 113/240] Fix postgres dump not working on development system (wrong user). --- script/backup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/backup b/script/backup index 618e81e16..8d259f323 100755 --- a/script/backup +++ b/script/backup @@ -61,7 +61,7 @@ module Backup attr_reader :dry_run, :verbose, :sql_dump_as_db_user def initialize(db_name, data_root, backup_root, - verbose: false, dry_run: true, sql_dump_as_db_user: false) + verbose: false, dry_run: true, sql_dump_as_db_user: nil) @db_name = db_name @backup_root = Pathname.new(backup_root) @data_root = Pathname.new(data_root) @@ -262,7 +262,7 @@ To continue, I try the command again using sudo. end def pg_user_switch - sql_dump_as_db_user ? %w(-U ontohub) : [] + sql_dump_as_db_user ? %W(-U #{sql_dump_as_db_user}) : [] end def self.backup_dirs_allowed_to_delete(entries) @@ -310,7 +310,7 @@ BACKUP_ROOT = end backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, - sql_dump_as_db_user: true, + sql_dump_as_db_user: on_development_system?(RAILS_ROOT) ? 'postgres' : 'ontohub', dry_run: false, verbose: true) case ARGV.first From 255e2c2477c832c7a785ae0ae77fd25add1e58e6 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 12 Oct 2015 17:00:44 +0200 Subject: [PATCH 114/240] Adjust the script to be run as root user. --- script/backup | 54 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/script/backup b/script/backup index 8d259f323..0d1561a6c 100755 --- a/script/backup +++ b/script/backup @@ -10,14 +10,14 @@ # * the postgres database # # Usage -# First note: Run this as the ontohub user, *not* as root. +# First note: Run this as the root user, e.g. with sudo. # To create a backup, run this script with the argument `create`: -# $ script/backup create +# # script/backup create # Then a backup named with the current date and time is created in the # backup directory (see below). # # To restore a backup, run this script with the argument `restore ` -# $ script/backup restore 2015-01-01_00-00 +# # script/backup restore 2015-01-01_00-00 # Then the selected backup is fully restored # # Backup directory @@ -61,12 +61,13 @@ module Backup attr_reader :dry_run, :verbose, :sql_dump_as_db_user def initialize(db_name, data_root, backup_root, - verbose: false, dry_run: true, sql_dump_as_db_user: nil) + verbose: false, dry_run: true, sql_dump_as_db_user: nil, user: nil) @db_name = db_name @backup_root = Pathname.new(backup_root) @data_root = Pathname.new(data_root) @data_root_basename = @data_root.basename.to_s @data_dirs = DATA_DIRS.map { |dir| File.join(@data_root_basename, dir) } + @user = user @dry_run = dry_run @verbose = verbose @@ -126,7 +127,8 @@ module Backup def create_sql_dump puts 'Creating SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_dump', *pg_user_switch, '-Fc', db_name, '-f', SQL_DUMP_FILE) + exec('pg_dump', *pg_user_switch, '-Fc', db_name, '-f', SQL_DUMP_FILE, + user: @user) end end @@ -134,7 +136,8 @@ module Backup puts 'Creating repository archive...' Dir.chdir(data_root.join('..')) do archive_file = backup_instance_dir.join(REPOSITORY_FILE) - exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *@data_dirs) + exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *@data_dirs, + user: @user) end end @@ -153,7 +156,8 @@ module Backup exec('pg_restore', '-n', 'public', '-c', *pg_user_switch, '-d', db_name, - SQL_DUMP_FILE) + SQL_DUMP_FILE, + user: @user) end end @@ -189,7 +193,7 @@ directories #{@data_dirs.join(' ')} to a temporary directory #{tmpdir}. This is used as a backup for the case of an error while restoring. To continue, I try the command again using sudo. MSG - exec('sudo', 'mv', *@data_dirs, tmpdir) + exec('mv', *@data_dirs, tmpdir, user: 'root') end def extract_archive @@ -215,7 +219,7 @@ As the current user I have no access to remove the temporary directory #{tmpdir}. To continue, I try the command again using sudo. MSG - exec('sudo', 'rm', '-r', tmpdir) + exec('rm', '-r', tmpdir, user: 'root') end def enable_maintenance_mode @@ -236,25 +240,22 @@ To continue, I try the command again using sudo. FileUtils.rm maintenance_file unless dry_run end - def exec(*args) + # Execute a command as the given user. + def exec(*args, user: nil) puts "[executing next command in #{Dir.getwd}]" if verbose out = args.join(' ') puts out if verbose - system(*args) unless dry_run - end - - def try_as_sudo_with_fallback(*args) - unless exec('sudo', *args) - sudo_not_given_fallback(*args) # Wrong sudo password + if !dry_run + if user + system(['su', '-', user, '-c', escape_arguments(args)]) + else + system(*args) + end end - rescue Exception => e - raise e unless e.is_a?(Interrupt) # Ctrl-C when asked for password - sudo_not_given_fallback(*args) end - def sudo_not_given_fallback(*args) - puts 'Super user privileges not granted. Trying as normal user.' - exec(*args) + def escape_arguments(args) + ([args[0]] + *args[1..-1].map { |a| %("#{a}")}).join(' ') end def maintenance_file @@ -285,9 +286,9 @@ def on_development_system?(rails_root) end # Don't allow this to be run as the root user. -if ENV['USER'] == 'root' - puts 'Running this script as the root user is disabled.' - puts 'Please run it as a normal user that has sudo privileges, e.g. ontohub.' +if ENV['USER'] != 'root' + puts 'Running this script as a normal user is disabled.' + puts 'Please run it as root.' exit end @@ -295,6 +296,8 @@ end RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') BACKUP_ROOT_PRODUCTION = '/local/home/ontohub/ontohub_data_backup' +USER = 'ontohub' + DATABASE = if on_development_system?(RAILS_ROOT) 'ontohub_development' @@ -311,6 +314,7 @@ BACKUP_ROOT = backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, sql_dump_as_db_user: on_development_system?(RAILS_ROOT) ? 'postgres' : 'ontohub', + user: USER, dry_run: false, verbose: true) case ARGV.first From 4fbbf628eda08ffdeb98cb41fb7e9001050eec3a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 26 Oct 2015 08:29:17 +0100 Subject: [PATCH 115/240] Use sudo again. --- script/backup | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index 0d1561a6c..cb295f47a 100755 --- a/script/backup +++ b/script/backup @@ -57,6 +57,9 @@ module Backup DATA_DIRS = %w(repositories git_daemon) + # Use 'sudo' on most systems + SUDO_BINARY = '+' + attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir attr_reader :dry_run, :verbose, :sql_dump_as_db_user @@ -246,14 +249,20 @@ To continue, I try the command again using sudo. out = args.join(' ') puts out if verbose if !dry_run - if user - system(['su', '-', user, '-c', escape_arguments(args)]) + if user == 'root' + system([sudo, *args]) + elsif user + system([sudo, 'su', '-', user, '-c', escape_arguments(args)]) else system(*args) end end end + def sudo + SUDO_BINARY + end + def escape_arguments(args) ([args[0]] + *args[1..-1].map { |a| %("#{a}")}).join(' ') end From a198c47a366da7f2bce639a52963875c71613ff1 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 26 Oct 2015 08:29:38 +0100 Subject: [PATCH 116/240] Escape double quotes in arguments. --- script/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/backup b/script/backup index cb295f47a..7fd07e227 100755 --- a/script/backup +++ b/script/backup @@ -264,7 +264,7 @@ To continue, I try the command again using sudo. end def escape_arguments(args) - ([args[0]] + *args[1..-1].map { |a| %("#{a}")}).join(' ') + ([args[0]] + *args[1..-1].map { |a| %("#{a.gsub('"', '\"')}")}).join(' ') end def maintenance_file From 7e19fab3af21714e410863295a27b859111ba40d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 10 Nov 2015 11:50:41 +0100 Subject: [PATCH 117/240] Lots of fixes. --- script/backup | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/script/backup b/script/backup index 7fd07e227..2d75d15d7 100755 --- a/script/backup +++ b/script/backup @@ -1,3 +1,5 @@ +#!/local/usr/ruby/shims/ruby +# The first line is for deployment machines only. For local machines, use: #!/usr/bin/env ruby # You can find more extensive documentation of this script at @@ -64,13 +66,15 @@ module Backup attr_reader :dry_run, :verbose, :sql_dump_as_db_user def initialize(db_name, data_root, backup_root, - verbose: false, dry_run: true, sql_dump_as_db_user: nil, user: nil) + verbose: false, dry_run: true, sql_dump_as_db_user: nil, + user: nil, group: nil) @db_name = db_name @backup_root = Pathname.new(backup_root) @data_root = Pathname.new(data_root) @data_root_basename = @data_root.basename.to_s @data_dirs = DATA_DIRS.map { |dir| File.join(@data_root_basename, dir) } @user = user + @group = group @dry_run = dry_run @verbose = verbose @@ -125,13 +129,15 @@ module Backup puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose # Create directory even in dry run to let the script continue. FileUtils.mkdir_p(backup_instance_dir) + puts "FileUtils.chown #{@user} #{@group} #{backup_instance_dir}" if verbose + FileUtils.chown(@user, @group, backup_instance_dir) end def create_sql_dump puts 'Creating SQL dump...' Dir.chdir(backup_instance_dir) do - exec('pg_dump', *pg_user_switch, '-Fc', db_name, '-f', SQL_DUMP_FILE, - user: @user) + exec('pg_dump', *pg_user_switch, '-Fc', db_name, + '-f', backup_instance_dir.join(SQL_DUMP_FILE), user: @user) end end @@ -245,16 +251,22 @@ To continue, I try the command again using sudo. # Execute a command as the given user. def exec(*args, user: nil) - puts "[executing next command in #{Dir.getwd}]" if verbose + print "[executing next command in #{Dir.getwd}" if verbose + print " as user #{user}" if verbose && user + puts "]" if verbose + out = args.join(' ') puts out if verbose if !dry_run if user == 'root' - system([sudo, *args]) + exec_system(*[sudo, *args]) elsif user - system([sudo, 'su', '-', user, '-c', escape_arguments(args)]) + # This looks strange because of the double sudo. + # It is needed on our deployment machines to get the environment right. + exec_system(*['+', 'sudo', '-u', user, 'bash', '-c', + "cd #{Dir.getwd} && #{escape_arguments(args)}"]) else - system(*args) + exec_system(*args) end end end @@ -263,8 +275,20 @@ To continue, I try the command again using sudo. SUDO_BINARY end + def exec_system(*args) + # puts args.join(' ') # For debugging + system(*args) + end + def escape_arguments(args) - ([args[0]] + *args[1..-1].map { |a| %("#{a.gsub('"', '\"')}")}).join(' ') + rest = args[1..-1].map do |arg| + if arg.to_s.include?(' ') + %("#{arg.gsub('"', '\"')}") + else + arg + end + end + ([args[0]] + rest).join(' ') end def maintenance_file @@ -306,6 +330,7 @@ RAILS_ROOT = Pathname.new(__FILE__).dirname.join('..') BACKUP_ROOT_PRODUCTION = '/local/home/ontohub/ontohub_data_backup' USER = 'ontohub' +GROUP = 'webservd' DATABASE = if on_development_system?(RAILS_ROOT) @@ -323,7 +348,7 @@ BACKUP_ROOT = backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, sql_dump_as_db_user: on_development_system?(RAILS_ROOT) ? 'postgres' : 'ontohub', - user: USER, + user: USER, group: GROUP, dry_run: false, verbose: true) case ARGV.first From a9b1a876554e3539bd8e70e6081888c8a0134f22 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 11 Nov 2015 14:08:02 +0100 Subject: [PATCH 118/240] Fix archive extraction. --- script/backup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/backup b/script/backup index 2d75d15d7..e4ef0ed44 100755 --- a/script/backup +++ b/script/backup @@ -214,8 +214,8 @@ they were before the backup. If you refuse to enter the password (Ctl-C) or enter a wrong password, only the permissions will not be restored and all restored files will belong to the current user/group. MSG - try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xf', - archive_file.to_s, *@data_dirs) + exec('tar', verbose ? 'vxf' : 'xf', archive_file.to_s, *@data_dirs, + user: 'root') end def remove_tmpdir(tmpdir) From b84d89b1aa3faa108105fc68925e33dc7eaf9d01 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 31 Dec 2015 12:16:53 +0100 Subject: [PATCH 119/240] Remove development system handling. --- script/backup | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/script/backup b/script/backup index e4ef0ed44..b5dd0b92e 100755 --- a/script/backup +++ b/script/backup @@ -23,8 +23,6 @@ # Then the selected backup is fully restored # # Backup directory -# For development machines, the backup directory is: -# /tmp/backup/ # And for production machines, the backup directory is: # /home/ontohub/ontohub_data_backup # @@ -306,16 +304,7 @@ To continue, I try the command again using sudo. end def data_root(rails_root) - if on_development_system?(rails_root) - File.realpath(rails_root.join('data')) - else - ENV['DATA_ROOT'] ||'/data/git' - end -end - -def on_development_system?(rails_root) - data_path = rails_root.join('data') - File.exist?(data_path) && !File.symlink?(data_path) + ENV['DATA_ROOT'] ||'/data/git' end # Don't allow this to be run as the root user. @@ -332,22 +321,12 @@ BACKUP_ROOT_PRODUCTION = '/local/home/ontohub/ontohub_data_backup' USER = 'ontohub' GROUP = 'webservd' -DATABASE = - if on_development_system?(RAILS_ROOT) - 'ontohub_development' - else - 'ontohub' - end +DATABASE = 'ontohub' -BACKUP_ROOT = - if on_development_system?(RAILS_ROOT) - RAILS_ROOT.join('tmp', 'backup') - else - File.realpath(BACKUP_ROOT_PRODUCTION) - end +BACKUP_ROOT = File.realpath(BACKUP_ROOT_PRODUCTION) backup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT, - sql_dump_as_db_user: on_development_system?(RAILS_ROOT) ? 'postgres' : 'ontohub', + sql_dump_as_db_user: 'ontohub', user: USER, group: GROUP, dry_run: false, verbose: true) From 108ccf954b02c757f7d267e5eb9260f71ff39862 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 31 Dec 2015 12:20:08 +0100 Subject: [PATCH 120/240] Add a dry run check --- script/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/backup b/script/backup index b5dd0b92e..0441d9aad 100755 --- a/script/backup +++ b/script/backup @@ -200,7 +200,7 @@ directories #{@data_dirs.join(' ')} to a temporary directory #{tmpdir}. This is used as a backup for the case of an error while restoring. To continue, I try the command again using sudo. MSG - exec('mv', *@data_dirs, tmpdir, user: 'root') + exec('mv', *@data_dirs, tmpdir, user: 'root') unless dry_run end def extract_archive From ab0efd0754ad1487560385983a635086fdd09084 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 31 Dec 2015 12:20:36 +0100 Subject: [PATCH 121/240] Clarify sudo usage in the comments. --- script/backup | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/backup b/script/backup index 0441d9aad..a7cd6b487 100755 --- a/script/backup +++ b/script/backup @@ -259,8 +259,9 @@ To continue, I try the command again using sudo. if user == 'root' exec_system(*[sudo, *args]) elsif user - # This looks strange because of the double sudo. + # This looks strange because of the combination of + and sudo. # It is needed on our deployment machines to get the environment right. + # On other machines, remove the call of +. exec_system(*['+', 'sudo', '-u', user, 'bash', '-c', "cd #{Dir.getwd} && #{escape_arguments(args)}"]) else From 9d5e52fe23ffbea4af186b81d2a3ebee14867be2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 31 Dec 2015 12:25:47 +0100 Subject: [PATCH 122/240] Update documentation. --- doc/backup_and_restore_of_ontohub_data.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/backup_and_restore_of_ontohub_data.md b/doc/backup_and_restore_of_ontohub_data.md index e93710bdd..66c287fd1 100644 --- a/doc/backup_and_restore_of_ontohub_data.md +++ b/doc/backup_and_restore_of_ontohub_data.md @@ -1,22 +1,23 @@ # Backup and Restore of Ontohub Data We have a backup script in `script/backup` that backs up all the data of the Ontohub instance or restores a backup. +It is tailored to the productive deployments of ontohub.org and cannot be used with development machines. # Backing up -To create a backup, simply run +To create a backup, simply run as `admin` user ```shell script/backup create ``` What happens is: * It activates the maintenance mode, which deactivates the Ontohub instance (by creating the file `data/maintenance.txt`). This way, the data gets frozen and the backup will have a consistent state. -* It creates a folder like `2014-12-06_20-06-37` in `/home/ontohub/ontohub_data_backup` on a production machine or in `ontohub/tmp/backup` on a development machine. -* In that folder, there is a `ontohub_repositories.tar.gz` created with the repository data inside. +* It creates a folder like `2014-12-06_20-06-37` in `~ontohub/ontohub_data_backup` on a production machine. +* In this folder, there is a `ontohub_repositories.tar.gz` created with the repository data inside. * Also, a `ontohub_sql_dump.postgresql` file is created, which is a compressed PostgreSQL dump. * It deactivates the maintenance mode (by deleting the `data/maintenance.txt`). # Restoring -To restore a previously created backup, run +To restore a previously created backup, run as `admin` user ```shell script/backup restore 2014-12-06_20-06-37 ``` @@ -28,7 +29,7 @@ Otherwise restoring the SQL dump will fail. What happens is: * It activates the maintenance mode. * It restores the PostgreSQL dump `ontohub_sql_dump.postgresql`. This drops and recreates the whole database. In order to drop the database, there must be no other active connection to it. -* It moves the current (pre-restore) repositories to a temporary directory (which one will be printed to stdout). This is done because the admin can do something with the repositories if restore fails. +* It moves the current (pre-restore) repositories to a temporary directory (which one will be printed to stdout). This is done because the admin can do something with the repositories if restoring the backup fails. * It extracts the repository archive `ontohub_repositories.tar.gz` to the Ontohub instance. This is first tried with `sudo` because the permissions/file mode are only restored if executed with super user rights. If cancelled (CTRL+C) or the wrong password is entered three times, the archive is extracted without `sudo`. The use of `sudo` is required to restore file permissions of the git repositories. If cancelled, the file permissions need to be set manually with `chown`. From 00c0602c40b14ad17b92e5d34a70945522e40220 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 11:43:55 +0100 Subject: [PATCH 123/240] Remove unneeded comment of ancestry. --- Gemfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Gemfile b/Gemfile index 389865fe8..c261fe81d 100644 --- a/Gemfile +++ b/Gemfile @@ -117,9 +117,6 @@ gem 'codemirror-rails', github: 'llwt/codemirror-rails' # API gem 'specroutes', github: '0robustus1/specroutes' -# Ancestry enabling tree structure in category model -# gem 'ancestry' - # Use dagnabit to model categories # Newer versions than 3.0.x are not compatible to rails 3.2 gem 'dagnabit', '~> 3.0.1' From 6d4ef892979ace580ed0a075609eb4acb3317209 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 5 Jan 2016 09:14:37 +0100 Subject: [PATCH 124/240] Hotfix: SInE depth calculation. --- app/models/sine_axiom_selection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/sine_axiom_selection.rb b/app/models/sine_axiom_selection.rb index 13f4036c4..3e020f8cb 100644 --- a/app/models/sine_axiom_selection.rb +++ b/app/models/sine_axiom_selection.rb @@ -120,7 +120,7 @@ def select_new_axioms(sentence, current_depth) return if depth_limit_reached?(current_depth) new_axioms = select_axioms_by_sentence(sentence) - @selected_axioms @selected_axioms += new_axioms - new_axioms.each { |axiom| select_new_axioms(axiom, current_depth + 2) } + new_axioms.each { |axiom| select_new_axioms(axiom, current_depth + 1) } end def select_axioms_by_sentence(sentence) From 86ba6a8b237b7be390f838fc272ff2ef9c795cba Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 19:44:40 +0100 Subject: [PATCH 125/240] Fix backup script calling command in the docs. --- doc/backup_and_restore_of_ontohub_data.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/backup_and_restore_of_ontohub_data.md b/doc/backup_and_restore_of_ontohub_data.md index 66c287fd1..c27a2f8ee 100644 --- a/doc/backup_and_restore_of_ontohub_data.md +++ b/doc/backup_and_restore_of_ontohub_data.md @@ -6,7 +6,7 @@ It is tailored to the productive deployments of ontohub.org and cannot be used w # Backing up To create a backup, simply run as `admin` user ```shell -script/backup create ++ su - -c '~ontohub/webapp/script/backup create' ``` What happens is: @@ -19,7 +19,7 @@ What happens is: # Restoring To restore a previously created backup, run as `admin` user ```shell -script/backup restore 2014-12-06_20-06-37 ++ su - -c '~ontohub/webapp/script/backup restore 2014-12-06_20-06-37' ``` (use your own backup name - it is a folder from the above-mentioned backup directory) From 7ae9f534961fb14c72064feb422749308c0c1058 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 14:11:18 +0100 Subject: [PATCH 126/240] Change clone hint. --- app/helpers/repositories_helper.rb | 4 ++-- app/views/repositories/_clone_url.html.haml | 11 +++-------- config/locales/en.yml | 8 +++++++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 51807a8cc..8c2fa5b0d 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -12,8 +12,8 @@ def clone_methods(visible: Repository::DEFAULT_CLONE_TYPE) # end.join(', ') # end - def clone_method_link(method) - link_to method, "##{method}", class: 'clone_method_link', data: {clone: method} + def clone_method_link(method, text) + link_to(text, "##{method}", class: 'clone_method_link', data: {clone: method}) end def clone_type diff --git a/app/views/repositories/_clone_url.html.haml b/app/views/repositories/_clone_url.html.haml index a41023ea9..6676e8764 100644 --- a/app/views/repositories/_clone_url.html.haml +++ b/app/views/repositories/_clone_url.html.haml @@ -1,14 +1,9 @@ .clone_urls - clone_methods.each do |(clone_type, visible)| %p.clone_url_block{data: {clone: clone_type, default_visible: visible.to_s}} - Clone with - %span= clone_type + = t('repository.clone.header', clone_type: clone_type) %input.form-control.clone-url{type: 'text', readonly: true, value: repository_clone_url(resource, clone_type: clone_type, port: nil)} %p - You can clone the repository with - = clone_method_link 'git' - (read-only) or - = clone_method_link 'ssh-git' - (read-write). + = t('repository.clone.text', git: clone_method_link('git', t('repository.clone.git')), ssh_git: clone_method_link('ssh-git', t('repository.clone.ssh_git'))).html_safe - if signed_in? and not current_user.keys.any? - = link_to 'Upload a SSH key.', [:new, :key] + = link_to t('repository.upload_ssh_key'), [:new, :key] diff --git a/config/locales/en.yml b/config/locales/en.yml index 26a1ba680..72c0fc9f2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -38,6 +38,12 @@ en: clone_list_prefix: There is also a complete list of clone_list_link: repository clone-urls repository: + clone: + header: Clone with %{clone_type} + text: You can clone the repository with %{git} (read-only) or %{ssh_git} (read-write, ssh-key required). + git: git + ssh_git: ssh-git + upload_ssh_key: Upload an SSH key. delete: Delete Repository delete_error: "Can't delete repository: It contains %{oms} that is imported by another repository." deleted: Deleted @@ -268,4 +274,4 @@ en: proof: axioms: Manual Axiom Selection info: - javascript_warning: JavaScript is deactivated in your browser. Some features are only accessible with active JavaScript. \ No newline at end of file + javascript_warning: JavaScript is deactivated in your browser. Some features are only accessible with active JavaScript. From 787127631abcd463c471486a393ec0981d3ab9b7 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 14:51:41 +0100 Subject: [PATCH 127/240] Rename symlink_path setting to git_daemon_path. --- app/models/repository/symlink.rb | 4 ++-- config/initializers/paths.rb | 6 +++--- config/settings.yml | 2 +- config/settings/test.yml | 2 +- db/seeds.rb | 2 +- lib/settings_validator.rb | 2 +- lib/tasks/databases.rake | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/models/repository/symlink.rb b/app/models/repository/symlink.rb index 81511b68e..456838bd2 100644 --- a/app/models/repository/symlink.rb +++ b/app/models/repository/symlink.rb @@ -10,13 +10,13 @@ module Repository::Symlink end def symlink_name - Ontohub::Application.config.symlink_path.join("#{path}.git") + Ontohub::Application.config.git_daemon_path.join("#{path}.git") end protected def symlink_update - Ontohub::Application.config.symlink_path.mkpath + Ontohub::Application.config.git_daemon_path.mkpath symlink_remove symlink_name.make_symlink local_path end diff --git a/config/initializers/paths.rb b/config/initializers/paths.rb index e87583013..b52056ce8 100644 --- a/config/initializers/paths.rb +++ b/config/initializers/paths.rb @@ -1,6 +1,6 @@ module PathsInitializer DEFAULT_PATHS = {git_repositories: 'repositories', - symlinks: 'git_daemon', + git_deamon: 'git_daemon', commits: 'commits'} class << self def expand(path) @@ -21,7 +21,7 @@ def prepare(path, fallback = nil) def empty_initialization(config) config.data_root = nil config.git_root = nil - config.symlink_path = nil + config.git_daemon_path = nil config.commits_path = nil end @@ -30,7 +30,7 @@ def empty_initialization(config) def perform_initialization(config) config.data_root = prepare(Settings.paths.data) config.git_root = prepare(Settings.paths.git_repositories, DEFAULT_PATHS[:git_repositories]) - config.symlink_path = prepare(Settings.paths.symlinks, DEFAULT_PATHS[:symlinks]) + config.git_daemon_path = prepare(Settings.paths.git_daemon, DEFAULT_PATHS[:git_daemon]) config.commits_path = prepare(Settings.paths.commits, DEFAULT_PATHS[:commits]) end end diff --git a/config/settings.yml b/config/settings.yml index 7ff885d5f..1ff373449 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -78,7 +78,7 @@ paths: # git repositories (names of repositories are numbers/ids) git_repositories: # nil - then data/repositories is used # named symlinks to the git repositories - symlinks: # nil - then data/git_daemon is used + git_daemon: # nil - then data/git_daemon is used # cache for files that needed to be checked out from the git repositories commits: # nil - then data/commits is used diff --git a/config/settings/test.yml b/config/settings/test.yml index a5cf636ec..09b206abf 100644 --- a/config/settings/test.yml +++ b/config/settings/test.yml @@ -8,5 +8,5 @@ action_mailer: paths: data: tmp/data git_repositories: tmp/data/git - symlinks: tmp/data/git_daemon + git_daemon: tmp/data/git_daemon commits: tmp/data/commits diff --git a/db/seeds.rb b/db/seeds.rb index a3a75acdb..fecb2c388 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -18,7 +18,7 @@ # Purge data directory FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_root.join('*'))) -FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.symlink_path.join('*'))) +FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_daemon_path.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.commits_path.join('*'))) # Include every .rb file inside db/seeds directory. diff --git a/lib/settings_validator.rb b/lib/settings_validator.rb index 14419c012..4b7a4a492 100644 --- a/lib/settings_validator.rb +++ b/lib/settings_validator.rb @@ -74,7 +74,7 @@ def format_initializer_error(key_portions) key = key_portions.join('.') if 'fqdn' == key "The FQDN could not be determined. Please set the hostname in #{CONFIG_YML_FILES}" - elsif %w(data_root git_root symlink_path commits_path).include?(key) + elsif %w(data_root git_root git_daemon_path commits_path).include?(key) 'Please check the paths keys in the settings -' else # other possible values: %w(consider_all_requests_local secret_token log_level) diff --git a/lib/tasks/databases.rake b/lib/tasks/databases.rake index 9ba8840c5..2a7cd7223 100644 --- a/lib/tasks/databases.rake +++ b/lib/tasks/databases.rake @@ -62,7 +62,7 @@ end def cleanup_git_folders FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_root.join('*'))) - FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.symlink_path.join('*'))) + FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_daemon_path.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.commits_path.join('*'))) end From 6bbb71b52f053c148669e5b128e236781150da1f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 15:15:53 +0100 Subject: [PATCH 128/240] Add setting for git_ssh path. --- config/initializers/paths.rb | 3 +++ config/settings.yml | 4 +++- config/settings/test.yml | 1 + db/seeds.rb | 1 + doc/productive_deployment.md | 8 ++++---- git/lib/git_shell.rb | 2 +- lib/settings_validator.rb | 2 +- lib/tasks/databases.rake | 1 + script/backup | 4 ++-- 9 files changed, 17 insertions(+), 9 deletions(-) diff --git a/config/initializers/paths.rb b/config/initializers/paths.rb index b52056ce8..9cd041e33 100644 --- a/config/initializers/paths.rb +++ b/config/initializers/paths.rb @@ -1,6 +1,7 @@ module PathsInitializer DEFAULT_PATHS = {git_repositories: 'repositories', git_deamon: 'git_daemon', + git_ssh: 'git_ssh', commits: 'commits'} class << self def expand(path) @@ -22,6 +23,7 @@ def empty_initialization(config) config.data_root = nil config.git_root = nil config.git_daemon_path = nil + config.git_ssh_path = nil config.commits_path = nil end @@ -31,6 +33,7 @@ def perform_initialization(config) config.data_root = prepare(Settings.paths.data) config.git_root = prepare(Settings.paths.git_repositories, DEFAULT_PATHS[:git_repositories]) config.git_daemon_path = prepare(Settings.paths.git_daemon, DEFAULT_PATHS[:git_daemon]) + config.git_ssh_path = prepare(Settings.paths.git_ssh, DEFAULT_PATHS[:git_ssh]) config.commits_path = prepare(Settings.paths.commits, DEFAULT_PATHS[:commits]) end end diff --git a/config/settings.yml b/config/settings.yml index 1ff373449..c3be8b586 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -77,8 +77,10 @@ paths: data: data # git repositories (names of repositories are numbers/ids) git_repositories: # nil - then data/repositories is used - # named symlinks to the git repositories + # named symlinks to the git repositories for git_daemon (only public) git_daemon: # nil - then data/git_daemon is used + # named symlinks to the git repositories for git-ssh (all repositories) + git_ssh: # nil - then data/git_ssh is used # cache for files that needed to be checked out from the git repositories commits: # nil - then data/commits is used diff --git a/config/settings/test.yml b/config/settings/test.yml index 09b206abf..45048d75a 100644 --- a/config/settings/test.yml +++ b/config/settings/test.yml @@ -9,4 +9,5 @@ paths: data: tmp/data git_repositories: tmp/data/git git_daemon: tmp/data/git_daemon + git_ssh: tmp/data/git_ssh commits: tmp/data/commits diff --git a/db/seeds.rb b/db/seeds.rb index fecb2c388..60f6cbf56 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -19,6 +19,7 @@ # Purge data directory FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_root.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_daemon_path.join('*'))) +FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_ssh_path.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.commits_path.join('*'))) # Include every .rb file inside db/seeds directory. diff --git a/doc/productive_deployment.md b/doc/productive_deployment.md index c826c308e..1fb78b585 100644 --- a/doc/productive_deployment.md +++ b/doc/productive_deployment.md @@ -354,10 +354,10 @@ after that you have to set up the git Deamon which will be explained next. cd /srv/http/ontohub/shared/data - mkdir -p git_daemon - chown ontohub:ontohub git_daemon - setfacl -m u:ontohub:rwx,d:u:ontohub:rwx git_daemon - setfacl -m g:ontohub:rwx,d:g:ontohub:rwx git_daemon + mkdir -p git_daemon git_ssh + chown ontohub:ontohub git_daemon git_ssh + setfacl -m u:ontohub:rwx,d:u:ontohub:rwx git_daemon git_ssh + setfacl -m g:ontohub:rwx,d:g:ontohub:rwx git_daemon git_ssh mkdir -p git_user/.ssh chmod 770 git_user/.ssh diff --git a/git/lib/git_shell.rb b/git/lib/git_shell.rb index 005394b03..eba6c2fcb 100644 --- a/git/lib/git_shell.rb +++ b/git/lib/git_shell.rb @@ -9,7 +9,7 @@ def initialize(key_id, command) @key_id = key_id @command = command # Here, Settings has added values from the PathsInitializer - @repos_path = Settings.symlink_path + @repos_path = Settings.git_ssh_path end def exec diff --git a/lib/settings_validator.rb b/lib/settings_validator.rb index 4b7a4a492..9e69b6ba0 100644 --- a/lib/settings_validator.rb +++ b/lib/settings_validator.rb @@ -74,7 +74,7 @@ def format_initializer_error(key_portions) key = key_portions.join('.') if 'fqdn' == key "The FQDN could not be determined. Please set the hostname in #{CONFIG_YML_FILES}" - elsif %w(data_root git_root git_daemon_path commits_path).include?(key) + elsif %w(data_root git_root git_daemon_path git_ssh_path commits_path).include?(key) 'Please check the paths keys in the settings -' else # other possible values: %w(consider_all_requests_local secret_token log_level) diff --git a/lib/tasks/databases.rake b/lib/tasks/databases.rake index 2a7cd7223..f2dd0bcbe 100644 --- a/lib/tasks/databases.rake +++ b/lib/tasks/databases.rake @@ -63,6 +63,7 @@ end def cleanup_git_folders FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_root.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_daemon_path.join('*'))) + FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.git_ssh_path.join('*'))) FileUtils.rm_rf(Dir.glob(Ontohub::Application.config.commits_path.join('*'))) end diff --git a/script/backup b/script/backup index a7cd6b487..c2b50daee 100755 --- a/script/backup +++ b/script/backup @@ -8,7 +8,7 @@ # Description # This backup script creates and restores backups of ontohub data. It includes: # * bare git repositories (data/repositories) -# * named symlinks to git repositories (data/git_daemon) +# * named symlinks to git repositories (data/git_daemon and data/git_ssh) # * the postgres database # # Usage @@ -55,7 +55,7 @@ module Backup SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql' REPOSITORY_FILE = 'ontohub_repositories.tar.gz' - DATA_DIRS = %w(repositories git_daemon) + DATA_DIRS = %w(repositories git_daemon git_ssh) # Use 'sudo' on most systems SUDO_BINARY = '+' From 9337e8c9590bbc8ad58d26bba312236c6328cc9d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 15:18:50 +0100 Subject: [PATCH 129/240] Rename concern to Symlinks. --- app/models/repository.rb | 2 +- app/models/repository/{symlink.rb => symlinks.rb} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/models/repository/{symlink.rb => symlinks.rb} (95%) diff --git a/app/models/repository.rb b/app/models/repository.rb index 7374fc24d..4f42cfdfd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -7,7 +7,7 @@ class Repository < ActiveRecord::Base include Repository::Importing include Repository::Ontologies include Repository::Scopes - include Repository::Symlink + include Repository::Symlinks include Repository::Validations DEFAULT_CLONE_TYPE = 'git' diff --git a/app/models/repository/symlink.rb b/app/models/repository/symlinks.rb similarity index 95% rename from app/models/repository/symlink.rb rename to app/models/repository/symlinks.rb index 456838bd2..44c6143ab 100644 --- a/app/models/repository/symlink.rb +++ b/app/models/repository/symlinks.rb @@ -1,7 +1,7 @@ # # Generates a symlink for exposing the repositories via git daemon # -module Repository::Symlink +module Repository::Symlinks extend ActiveSupport::Concern included do From f55fc9b4d9bf2d78c5c7fac976c948d618c24e5d Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 15:30:24 +0100 Subject: [PATCH 130/240] Rename symlink_path setting to git_daemon_path.#2 --- config/initializers/paths.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/paths.rb b/config/initializers/paths.rb index 9cd041e33..2bc7f45b0 100644 --- a/config/initializers/paths.rb +++ b/config/initializers/paths.rb @@ -1,6 +1,6 @@ module PathsInitializer DEFAULT_PATHS = {git_repositories: 'repositories', - git_deamon: 'git_daemon', + git_daemon: 'git_daemon', git_ssh: 'git_ssh', commits: 'commits'} class << self From e69dac398e3f5069608244463c3a28cce0888172 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 15:30:45 +0100 Subject: [PATCH 131/240] Move symlink creation code to Symlinks concern. --- app/models/repository/git.rb | 6 +----- app/models/repository/symlinks.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index 863f84002..1978985e5 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -27,11 +27,7 @@ def walk_commits(*args, &block) def create_and_init_git git - symlink_name = local_path.join("hooks") - symlink_name.rmtree - symlink_name.make_symlink(Rails.root.join('git','hooks'). - # replace capistrano-style release with 'current'-symlink - sub(%r{/releases/\d+/}, '/current/')) + create_hooks_symlink end def destroy_git diff --git a/app/models/repository/symlinks.rb b/app/models/repository/symlinks.rb index 44c6143ab..2cab1cd4d 100644 --- a/app/models/repository/symlinks.rb +++ b/app/models/repository/symlinks.rb @@ -15,6 +15,14 @@ def symlink_name protected + def create_hooks_symlink + hooks_symlink_name = local_path.join("hooks") + hooks_symlink_name.rmtree + hooks_symlink_name.make_symlink(Rails.root.join('git','hooks'). + # replace capistrano-style release with 'current'-symlink + sub(%r{/releases/\d+/}, '/current/')) + end + def symlink_update Ontohub::Application.config.git_daemon_path.mkpath symlink_remove From d4b472fb1676b6372adfede2256493546738062a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 16:18:42 +0100 Subject: [PATCH 132/240] Manage git_daemon and git_ssh symlink. --- app/models/repository/symlinks.rb | 35 ++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/app/models/repository/symlinks.rb b/app/models/repository/symlinks.rb index 2cab1cd4d..c006ec282 100644 --- a/app/models/repository/symlinks.rb +++ b/app/models/repository/symlinks.rb @@ -5,12 +5,17 @@ module Repository::Symlinks extend ActiveSupport::Concern included do - after_save :symlink_update, if: :path_changed? - before_destroy :symlink_remove + after_save :symlinks_update, if: :path_changed? + before_destroy :symlinks_remove end - def symlink_name - Ontohub::Application.config.git_daemon_path.join("#{path}.git") + SUPPORTED_LINK_CATEGORIES = %i(git_daemon git_ssh) + + def symlink_path(category) + unless SUPPORTED_LINK_CATEGORIES.include?(category) + raise "Unsupported symlink category: #{category.inspect}" + end + Ontohub::Application.config.send(:"#{category}_path").join("#{path}.git") end protected @@ -23,14 +28,24 @@ def create_hooks_symlink sub(%r{/releases/\d+/}, '/current/')) end - def symlink_update - Ontohub::Application.config.git_daemon_path.mkpath - symlink_remove - symlink_name.make_symlink local_path + def symlinks_update + create_cloning_symlink(:git_daemon) if public_r? || public_rw? + create_cloning_symlink(:git_ssh) + end + + def symlinks_remove + SUPPORTED_LINK_CATEGORIES.each do |category| + remove_cloning_symlink(category) + end end - def symlink_remove - symlink_name.unlink if symlink_name.exist? + def create_cloning_symlink(category) + symlink_path(category).join('..').mkpath + remove_cloning_symlink(category) + symlink_path(category).make_symlink(local_path) end + def remove_cloning_symlink(category) + symlink_path(category).unlink if symlink_path(category).exist? + end end From 314227c349d7c3a2aec4b39e11d6770b3db8a652 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 16:19:03 +0100 Subject: [PATCH 133/240] Add specs for git_daemon and git_ssh symlink management. --- spec/models/repository/symlink_spec.rb | 17 ------- spec/models/repository/symlinks_spec.rb | 67 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 17 deletions(-) delete mode 100644 spec/models/repository/symlink_spec.rb create mode 100644 spec/models/repository/symlinks_spec.rb diff --git a/spec/models/repository/symlink_spec.rb b/spec/models/repository/symlink_spec.rb deleted file mode 100644 index 3b331c8b7..000000000 --- a/spec/models/repository/symlink_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe Repository::Symlink do - - describe 'repository created' do - let(:repository){ create :repository } - let(:link){ File.readlink(repository.symlink_name) } - - it("symlink created"){ link.should == repository.local_path.to_s } - - describe 'repository destroy' do - before { repository.destroy } - it("symlink removed"){ repository.symlink_name.exist?.should be(false) } - end - end - -end diff --git a/spec/models/repository/symlinks_spec.rb b/spec/models/repository/symlinks_spec.rb new file mode 100644 index 000000000..4d62debf4 --- /dev/null +++ b/spec/models/repository/symlinks_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Repository::Symlinks do + context 'public repository' do + let(:repository) { create :repository } + + shared_examples 'symlink_creation' do |category| + let(:link_path) { repository.symlink_path(category) } + let(:link_target) { File.readlink(link_path) } + + it "symlink for #{category} created" do + expect(link_target).to eq(repository.local_path.to_s) + end + + context 'repository destroy' do + before { repository.destroy } + + it "symlink for #{category} removed" do + expect(repository.symlink_path(category).exist?).to be(false) + end + end + end + + include_examples('symlink_creation', :git_daemon) + include_examples('symlink_creation', :git_ssh) + end + + context 'private repository' do + let(:repository) { create :repository, access: 'private_rw' } + + context 'git_daemon' do + let(:category) { :git_daemon } + let(:link_path) { repository.symlink_path(category) } + let(:link_target) { File.readlink(link_path) } + + it "symlink for git_daemon not created" do + expect(repository.symlink_path(category).exist?).to be(false) + end + + context 'repository destroy' do + before { repository.destroy } + + it "symlink for git_daemon removed" do + expect(repository.symlink_path(category).exist?).to be(false) + end + end + end + + context 'git_ssh' do + let(:category) { :git_ssh } + let(:link_path) { repository.symlink_path(category) } + let(:link_target) { File.readlink(link_path) } + + it "symlink for git_ssh created" do + expect(link_target).to eq(repository.local_path.to_s) + end + + context 'repository destroy' do + before { repository.destroy } + + it "symlink for git_ssh removed" do + expect(repository.symlink_path(category).exist?).to be(false) + end + end + end + end +end From ee3789e4a12c51590a217d0d74baa98e9b31d30a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 16:43:33 +0100 Subject: [PATCH 134/240] Add data migration to recreate symlinks. --- ...20160107154144_recreate_repository_symlinks.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 db/data/20160107154144_recreate_repository_symlinks.rb diff --git a/db/data/20160107154144_recreate_repository_symlinks.rb b/db/data/20160107154144_recreate_repository_symlinks.rb new file mode 100644 index 000000000..106ba48ca --- /dev/null +++ b/db/data/20160107154144_recreate_repository_symlinks.rb @@ -0,0 +1,15 @@ +class RecreateRepositorySymlinks < ActiveRecord::Migration + def self.up + Repository.find_each do |repository| + repository.send(:symlinks_remove) + repository.send(:symlinks_update) + end + end + + def self.down + Repository.find_each do |repository| + repository.send(:symlinks_remove) + repository.send(:symlinks_update) + end + end +end From 98ff97ecba1886772d1380e4b01ebe5f1dccac9f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 16:47:16 +0100 Subject: [PATCH 135/240] Cleanup capistrano release path. --- app/models/repository/symlinks.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/repository/symlinks.rb b/app/models/repository/symlinks.rb index c006ec282..f99aa379b 100644 --- a/app/models/repository/symlinks.rb +++ b/app/models/repository/symlinks.rb @@ -23,9 +23,8 @@ def symlink_path(category) def create_hooks_symlink hooks_symlink_name = local_path.join("hooks") hooks_symlink_name.rmtree - hooks_symlink_name.make_symlink(Rails.root.join('git','hooks'). - # replace capistrano-style release with 'current'-symlink - sub(%r{/releases/\d+/}, '/current/')) + hooks_symlink_name. + make_symlink(cleanup_release(Rails.root.join('git','hooks'))) end def symlinks_update @@ -42,10 +41,15 @@ def symlinks_remove def create_cloning_symlink(category) symlink_path(category).join('..').mkpath remove_cloning_symlink(category) - symlink_path(category).make_symlink(local_path) + symlink_path(category).make_symlink(cleanup_release(local_path)) end def remove_cloning_symlink(category) symlink_path(category).unlink if symlink_path(category).exist? end + + # Replace capistrano-style release with 'current'-symlink. + def cleanup_release(path) + PathsInitializer.cleanup_release(path) + end end From 2263cf4f66c204601c9c1268fc55b4f7b5178424 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 19:49:25 +0100 Subject: [PATCH 136/240] Extract SequentialOntologyBatchParseWorker into a separate file. --- lib/ontology_batch_parse_worker.rb | 20 ------------------- lib/sequential_ontology_batch_parse_worker.rb | 17 ++++++++++++++++ 2 files changed, 17 insertions(+), 20 deletions(-) create mode 100644 lib/sequential_ontology_batch_parse_worker.rb diff --git a/lib/ontology_batch_parse_worker.rb b/lib/ontology_batch_parse_worker.rb index eccf58fca..2fb65e6d6 100644 --- a/lib/ontology_batch_parse_worker.rb +++ b/lib/ontology_batch_parse_worker.rb @@ -48,24 +48,4 @@ def handle_concurrency_issue end true end - -end - -class SequentialOntologyBatchParseWorker < OntologyBatchParseWorker - sidekiq_options queue: 'sequential' - - def perform(*args, try_count: 1) - establish_arguments(args, try_count: try_count) - ConcurrencyBalancer.sequential_lock do - execute_perform(try_count, args.first) - end - rescue ConcurrencyBalancer::AlreadyLockedError - handle_concurrency_issue - end - - def handle_concurrency_issue - SequentialOntologyBatchParseWorker. - perform_async(*@args, try_count: @try_count + 1) - end - end diff --git a/lib/sequential_ontology_batch_parse_worker.rb b/lib/sequential_ontology_batch_parse_worker.rb new file mode 100644 index 000000000..0b33f002a --- /dev/null +++ b/lib/sequential_ontology_batch_parse_worker.rb @@ -0,0 +1,17 @@ +class SequentialOntologyBatchParseWorker < OntologyBatchParseWorker + sidekiq_options queue: 'sequential' + + def perform(*args, try_count: 1) + establish_arguments(args, try_count: try_count) + ConcurrencyBalancer.sequential_lock do + execute_perform(try_count, args.first) + end + rescue ConcurrencyBalancer::AlreadyLockedError + handle_concurrency_issue + end + + def handle_concurrency_issue + SequentialOntologyBatchParseWorker. + perform_async(*@args, try_count: @try_count + 1) + end +end From e1256728bee0970ed34fe96c8b6b02f7a21ade90 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 5 Jan 2016 15:05:41 +0100 Subject: [PATCH 137/240] Remove method that overwrites sidekiq options The removed comment above the method is not only misleading but also wrong. get_sidekiq_options does not need to be defined here because it is defined in the included module Sidekiq::Worker. It returns what was defined by sidekiq_options. This method overwrote the sidekiq_options which set the queue to 'hets'. --- lib/worker.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/worker.rb b/lib/worker.rb index 08a22ee52..7eb71e416 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -3,6 +3,7 @@ # Worker for Sidekiq class Worker < BaseWorker sidekiq_options queue: 'hets' + sidekiq_options backtrace: true # Because of the JSON-Parsing the hash which contains # the try_count will contain the try_count key @@ -40,14 +41,6 @@ def handle_concurrency_issue self.class.perform_async(*@args, try_count: @try_count+1) end end - - # This method definition is required by sidekiq - def self.get_sidekiq_options - { - 'backtrace' => true - } - end - end class SequentialWorker < Worker From f41341c62c656e0c5ac40b281a7dfa5f90982748 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 5 Jan 2016 15:11:01 +0100 Subject: [PATCH 138/240] Retry 3x on failure not caused by concurrency. --- lib/worker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/worker.rb b/lib/worker.rb index 7eb71e416..432923856 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -3,6 +3,7 @@ # Worker for Sidekiq class Worker < BaseWorker sidekiq_options queue: 'hets' + sidekiq_options retry: 3 sidekiq_options backtrace: true # Because of the JSON-Parsing the hash which contains From 0c0319d417578bc9cfa723708ee451db7fb2d6d5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 09:43:51 +0100 Subject: [PATCH 139/240] Move import mapping handling to a concern. --- app/models/ontology.rb | 33 +--------------------- app/models/ontology/import_mappings.rb | 39 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 32 deletions(-) create mode 100644 app/models/ontology/import_mappings.rb diff --git a/app/models/ontology.rb b/app/models/ontology.rb index ab3906b6a..56b76da23 100644 --- a/app/models/ontology.rb +++ b/app/models/ontology.rb @@ -14,6 +14,7 @@ class Ontology < ActiveRecord::Base include Ontology::FileExtensions include Ontology::HetsOptions include Ontology::Import + include Ontology::ImportMappings include Ontology::Mappings include Ontology::Oops include Ontology::OwlClasses @@ -81,18 +82,6 @@ def title name? ? iri : nil end - def is_imported? - import_mappings.present? - end - - def is_imported_from_other_repository? - import_mappings_from_other_repositories.present? - end - - def imported_by - import_mappings.map(&:source) - end - def destroy_with_parent(user) if parent repository.delete_file(parent.path, user, @@ -128,21 +117,11 @@ def can_be_deleted_with_whole_repository? !is_imported_from_other_repository? end - def imported_ontologies - fetch_mappings_by_kind(self, 'import') - end - def contains_logic_translations? query, args = contains_logic_translations_query(self) pluck_select([query, *args], :logically_translated).size > 1 end - def direct_imported_ontologies - ontology_ids = Mapping.where(target_id: self, kind: 'import'). - pluck(:source_id) - Ontology.where(id: ontology_ids) - end - def combined_sentences affected_ontology_ids = [self.id] + imported_ontologies.pluck(:id) Sentence.original.where(ontology_id: affected_ontology_ids) @@ -212,14 +191,4 @@ def has_file(commit_oid = nil) repository.path_exists?(path, commit_oid) end end - - protected - - def import_mappings - Mapping.where(source_id: id, kind: 'import') - end - - def import_mappings_from_other_repositories - import_mappings.select { |l| l.target.repository != repository } - end end diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb new file mode 100644 index 000000000..5c6c30ecc --- /dev/null +++ b/app/models/ontology/import_mappings.rb @@ -0,0 +1,39 @@ +class Ontology + module ImportMappings + extend ActiveSupport::Concern + + included do + def is_imported? + import_mappings.present? + end + + def is_imported_from_other_repository? + import_mappings_from_other_repositories.present? + end + + def imported_by + import_mappings.map(&:source) + end + + def imported_ontologies + fetch_mappings_by_kind(self, 'import') + end + + def direct_imported_ontologies + ontology_ids = Mapping.where(target_id: self, kind: 'import'). + pluck(:source_id) + Ontology.where(id: ontology_ids) + end + + protected + + def import_mappings + Mapping.where(source_id: id, kind: 'import') + end + + def import_mappings_from_other_repositories + import_mappings.select { |l| l.target.repository != repository } + end + end + end +end From 006de713cc0c62dc3dd8549f4d74c00e8b822dd2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 12:46:38 +0100 Subject: [PATCH 140/240] Move ontology saving code into separate service class. --- app/models/repository/git.rb | 150 +------------------- app/models/repository/importing.rb | 8 +- lib/ontology_saver.rb | 155 +++++++++++++++++++++ lib/repository_update_worker.rb | 9 +- spec/lib/repository/git_repository_spec.rb | 3 +- 5 files changed, 169 insertions(+), 156 deletions(-) create mode 100644 lib/ontology_saver.rb diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index 863f84002..2391d04a6 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -41,7 +41,8 @@ def destroy_git def delete_file(filepath, user, message = nil, &block) commit_oid = git.delete_file(user_info(user), filepath, &block) commit_for!(commit_oid).commit_oid - mark_ontology_as_having_file(filepath, has_file: false) + OntologySaver.new(self). + mark_ontology_as_having_file(filepath, has_file: false) end def save_file(tmp_file, filepath, message, user, do_not_parse: false) @@ -50,7 +51,8 @@ def save_file(tmp_file, filepath, message, user, do_not_parse: false) git.add_file(user_info(user), tmp_file, filepath, message) do |commit_oid| ontology_version_options = OntologyVersionOptions.new( filepath, user, do_not_parse: do_not_parse) - version = save_ontology(commit_oid, ontology_version_options) + version = OntologySaver.new(self). + save_ontology(commit_oid, ontology_version_options) end touch version @@ -66,79 +68,6 @@ def save_file_only(tmp_file, filepath, message, user) commit end - def save_ontology(commit_oid, ontology_version_options) - # we expect that this method is only called, when the ontology is 'present' - return unless Ontology.file_extensions.include?( - File.extname(ontology_version_options.filepath)) - - ontology = find_or_create_ontology(ontology_version_options) - - return unless master_file?(ontology, ontology_version_options) - return if ontology.versions.find_by_commit_oid(commit_oid) - - version = create_version(ontology, commit_oid, ontology_version_options) - ontology.present = true - ontology.save! - - version - end - - def find_or_create_ontology(ontology_version_options) - ontology = find_existing_ontology(ontology_version_options) - - if !ontology - basepath = File.basepath(ontology_version_options.filepath) - locid = generate_locid(basepath) - ontology = create_ontology(ontology_version_options.filepath, locid) - end - - ontology - end - - def find_existing_ontology(ontology_version_options) - ontologies.with_basepath( - File.basepath(ontology_version_options.pre_saving_filepath)). - without_parent.first - end - - def create_ontology(filepath, locid) - ontology = corresponding_ontology_klass(filepath).new - - ontology.basepath = File.basepath(filepath) - ontology.file_extension = File.extname(filepath) - ontology.locid = locid - ontology.name = filepath.split('/')[-1].split(".")[0].capitalize - ontology.repository = self - ontology.present = true - ontology.save! - - ontology - end - - def corresponding_ontology_klass(filepath) - is_distributed = Ontology.file_extensions_distributed. - include?(File.extname(filepath)) - is_distributed ? DistributedOntology : SingleOntology - end - - def create_version(ontology, commit_oid, ontology_version_options) - version = ontology.versions.build( - { commit_oid: commit_oid, - user: ontology_version_options.user, - commit: commit_for!(commit_oid), - # We can't use the ontology's filepath bacause it might have changed - basepath: File.basepath(ontology_version_options.filepath), - file_extension: File.extname(ontology_version_options.filepath), - fast_parse: ontology_version_options.fast_parse }, - { without_protection: true }) - version.do_not_parse! if ontology_version_options.do_not_parse - version.save! - ontology.ontology_version = version - ontology.save! - - version - end - def commit_for!(commit_oid) instance = Commit.where(repository_id: self, commit_oid: commit_oid). first_or_initialize @@ -163,73 +92,6 @@ def recent_changes walk_commits(limit: 3) end - def suspended_save_ontologies(options={}) - versions = [] - commits_count = 0 - highest_change_file_count = 0 - walk_commits(options) { |commit| - commits_count += 1 - current_file_count = 0 - git.changed_files(commit.oid).each { |f| - current_file_count += 1 - if f.added? || f.modified? - mark_ontology_as_having_file(f.path, has_file: true) - ontology_version_options = OntologyVersionOptions.new( - f.path, - options.delete(:user), - fast_parse: has_changed?(f.path, commit.oid), - do_not_parse: true) - versions << save_ontology(commit.oid, ontology_version_options) - elsif f.renamed? - ontology_version_options = OntologyVersionOptions.new( - f.path, - options.delete(:user), - fast_parse: has_changed?(f.path, commit.oid), - do_not_parse: true, - previous_filepath: f.delta.old_file[:path]) - versions << save_ontology(commit.oid, ontology_version_options) - elsif f.deleted? - mark_ontology_as_having_file(f.path, has_file: false) - end - } - highest_change_file_count = [highest_change_file_count, - current_file_count].max - } - - priority = applicable_for_priority?(commits_count, - highest_change_file_count) - schedule_batch_parsing(versions, priority_mode: priority) - end - - def schedule_batch_parsing(versions, priority_mode: false) - grouped_versions = versions.compact.group_by(&:path) - grouped_versions.each do |k,versions| - optioned_versions = versions.map do |version| - [version.id, { fast_parse: version.fast_parse }] - end - OntologyBatchParseWorker. - perform_async_with_priority(priority_mode, optioned_versions) - end - end - - def applicable_for_priority?(commits_count, highest_change_file_count) - (commits_count <= priority_settings.commits) && - (highest_change_file_count <= priority_settings.changed_files_per_commit) - end - - def priority_settings - @priority_settings ||= OpenStruct.new(Settings.git[:push_priority]) - end - - def mark_ontology_as_having_file(path, has_file: false) - ontos = ontologies.with_path(path) - return unless ontos.any? { |onto| onto.has_file != has_file } - ontos.each do |onto| - onto.has_file = has_file - onto.save - end - end - def user_info(user) if user {email: user.email, name: user.name} @@ -242,8 +104,4 @@ def user_info(user) def master_file?(ontology, ontology_version_options) ontology.path == ontology_version_options.pre_saving_filepath end - - def generate_locid(basepath) - "/#{path}/#{basepath}" - end end diff --git a/app/models/repository/importing.rb b/app/models/repository/importing.rb index e3c949e08..4c6c04d1c 100644 --- a/app/models/repository/importing.rb +++ b/app/models/repository/importing.rb @@ -77,10 +77,10 @@ def process_fetch(method, args, remote_type) end def update_database_after_fetch(range) - suspended_save_ontologies \ - start_oid: range.current, - stop_oid: range.previous, - walk_order: :reverse + OntologySaver.new(self). + suspended_save_ontologies(start_oid: range.current, + stop_oid: range.previous, + walk_order: :reverse) end module ClassMethods diff --git a/lib/ontology_saver.rb b/lib/ontology_saver.rb new file mode 100644 index 000000000..0eab87049 --- /dev/null +++ b/lib/ontology_saver.rb @@ -0,0 +1,155 @@ +# A service class for saving an ontology, e.g. when its file is added to +# the git repository. +class OntologySaver + attr_accessor :repository + + def initialize(repository) + self.repository = repository + end + + def save_ontology(commit_oid, ontology_version_options) + # we expect that this method is only called, when the ontology is 'present' + return unless Ontology.file_extensions.include?( + File.extname(ontology_version_options.filepath)) + + ontology = find_or_create_ontology(ontology_version_options) + + return unless repository.master_file?(ontology, ontology_version_options) + return if ontology.versions.find_by_commit_oid(commit_oid) + + version = create_version(ontology, commit_oid, ontology_version_options) + ontology.present = true + ontology.save! + + version + end + + def suspended_save_ontologies(options={}) + versions = [] + commits_count = 0 + highest_change_file_count = 0 + repository.walk_commits(options) { |commit| + commits_count += 1 + current_file_count = 0 + repository.git.changed_files(commit.oid).each { |f| + current_file_count += 1 + if f.added? || f.modified? + mark_ontology_as_having_file(f.path, has_file: true) + ontology_version_options = OntologyVersionOptions.new( + f.path, + options.delete(:user), + fast_parse: repository.has_changed?(f.path, commit.oid), + do_not_parse: true) + versions << save_ontology(commit.oid, ontology_version_options) + elsif f.renamed? + ontology_version_options = OntologyVersionOptions.new( + f.path, + options.delete(:user), + fast_parse: repository.has_changed?(f.path, commit.oid), + do_not_parse: true, + previous_filepath: f.delta.old_file[:path]) + versions << save_ontology(commit.oid, ontology_version_options) + elsif f.deleted? + mark_ontology_as_having_file(f.path, has_file: false) + end + } + highest_change_file_count = [highest_change_file_count, + current_file_count].max + } + + priority = applicable_for_priority?(commits_count, + highest_change_file_count) + schedule_batch_parsing(versions, priority_mode: priority) + end + + def mark_ontology_as_having_file(path, has_file: false) + ontos = repository.ontologies.with_path(path) + return unless ontos.any? { |onto| onto.has_file != has_file } + ontos.each do |onto| + onto.has_file = has_file + onto.save + end + end + + protected + + def find_or_create_ontology(ontology_version_options) + ontology = find_existing_ontology(ontology_version_options) + + if !ontology + basepath = File.basepath(ontology_version_options.filepath) + locid = generate_locid(basepath) + ontology = create_ontology(ontology_version_options.filepath, locid) + end + + ontology + end + + def find_existing_ontology(ontology_version_options) + repository.ontologies.with_basepath( + File.basepath(ontology_version_options.pre_saving_filepath)). + without_parent.first + end + + def create_ontology(filepath, locid) + ontology = corresponding_ontology_klass(filepath).new + + ontology.basepath = File.basepath(filepath) + ontology.file_extension = File.extname(filepath) + ontology.locid = locid + ontology.name = filepath.split('/')[-1].split(".")[0].capitalize + ontology.repository = repository + ontology.present = true + ontology.save! + + ontology + end + + def corresponding_ontology_klass(filepath) + is_distributed = Ontology.file_extensions_distributed. + include?(File.extname(filepath)) + is_distributed ? DistributedOntology : SingleOntology + end + + def create_version(ontology, commit_oid, ontology_version_options) + version = ontology.versions.build( + { commit_oid: commit_oid, + user: ontology_version_options.user, + commit: repository.commit_for!(commit_oid), + # We can't use the ontology's filepath bacause it might have changed + basepath: File.basepath(ontology_version_options.filepath), + file_extension: File.extname(ontology_version_options.filepath), + fast_parse: ontology_version_options.fast_parse }, + { without_protection: true }) + version.do_not_parse! if ontology_version_options.do_not_parse + version.save! + ontology.ontology_version = version + ontology.save! + + version + end + + def applicable_for_priority?(commits_count, highest_change_file_count) + (commits_count <= priority_settings.commits) && + (highest_change_file_count <= priority_settings.changed_files_per_commit) + end + + def priority_settings + @priority_settings ||= OpenStruct.new(Settings.git[:push_priority]) + end + + def schedule_batch_parsing(versions, priority_mode: false) + grouped_versions = versions.compact.group_by(&:path) + grouped_versions.each do |k,versions| + optioned_versions = versions.map do |version| + [version.id, { fast_parse: version.fast_parse }] + end + OntologyBatchParseWorker. + perform_async_with_priority(priority_mode, optioned_versions) + end + end + + def generate_locid(basepath) + "/#{repository.path}/#{basepath}" + end +end diff --git a/lib/repository_update_worker.rb b/lib/repository_update_worker.rb index 5328df226..66c6a8762 100644 --- a/lib/repository_update_worker.rb +++ b/lib/repository_update_worker.rb @@ -10,11 +10,10 @@ class RepositoryUpdateWorker < Worker def perform(repo_path, oldrev, newrev, refname, key_id) repo_path =~ /(\d+)\/?\z/ repo_id = $1.to_i - Repository.where(id: repo_id).first! - .suspended_save_ontologies \ - start_oid: newrev, - stop_oid: oldrev, - walk_order: :reverse + OntologySaver.new(Repository.where(id: repo_id).first!). + suspended_save_ontologies(start_oid: newrev, + stop_oid: oldrev, + walk_order: :reverse) end end diff --git a/spec/lib/repository/git_repository_spec.rb b/spec/lib/repository/git_repository_spec.rb index b880f1778..3ecbff261 100644 --- a/spec/lib/repository/git_repository_spec.rb +++ b/spec/lib/repository/git_repository_spec.rb @@ -41,7 +41,8 @@ FileUtils.rm_r(path) FileUtils.mv(bare_git.path, path) Sidekiq::Testing.fake! do - repository.suspended_save_ontologies(walk_order: Rugged::SORT_REVERSE) + OntologySaver.new(repository). + suspended_save_ontologies(walk_order: Rugged::SORT_REVERSE) end end From adcdabf30111f412ca84d30e253dcec7bbae64f5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 12:47:19 +0100 Subject: [PATCH 141/240] Move SequentialWorker to a separate file. --- lib/sequential_worker.rb | 16 ++++++++++++++++ lib/worker.rb | 18 ------------------ 2 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 lib/sequential_worker.rb diff --git a/lib/sequential_worker.rb b/lib/sequential_worker.rb new file mode 100644 index 000000000..aee2d4f35 --- /dev/null +++ b/lib/sequential_worker.rb @@ -0,0 +1,16 @@ +class SequentialWorker < Worker + sidekiq_options queue: 'sequential' + + def perform(*args, try_count: 1) + establish_arguments(args, try_count: try_count) + ConcurrencyBalancer.sequential_lock do + execute_perform(try_count, *args) + end + rescue ConcurrencyBalancer::AlreadyLockedError + handle_concurrency_issue + end + + def handle_concurrency_issue + SequentialWorker.perform_async(*@args, try_count: @try_count+1) + end +end diff --git a/lib/worker.rb b/lib/worker.rb index 432923856..654ed2e76 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -43,21 +43,3 @@ def handle_concurrency_issue end end end - -class SequentialWorker < Worker - sidekiq_options queue: 'sequential' - - def perform(*args, try_count: 1) - establish_arguments(args, try_count: try_count) - ConcurrencyBalancer.sequential_lock do - execute_perform(try_count, *args) - end - rescue ConcurrencyBalancer::AlreadyLockedError - handle_concurrency_issue - end - - def handle_concurrency_issue - SequentialWorker.perform_async(*@args, try_count: @try_count+1) - end - -end From ddf2c8613a6e7137e2df8e6d845793ba71883664 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:18:12 +0100 Subject: [PATCH 142/240] Parse additional files asynchronously. --- app/models/ontology_version/parsing.rb | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/models/ontology_version/parsing.rb b/app/models/ontology_version/parsing.rb index 2f2baa390..c8a5f8de4 100644 --- a/app/models/ontology_version/parsing.rb +++ b/app/models/ontology_version/parsing.rb @@ -7,6 +7,7 @@ module OntologyVersion::Parsing after_create :async_parse, :if => :commit_oid? attr_accessor :fast_parse + attr_accessor :files_to_parse_afterwards end def do_not_parse! @@ -18,14 +19,15 @@ def async_parse(*args) update_state! :pending if @fast_parse - async :parse_fast + async :parse_fast, files_to_parse_afterwards else - async :parse_full + async :parse_full, files_to_parse_afterwards end end end - def parse(refresh_cache: false, structure_only: self.fast_parse) + def parse(refresh_cache: false, structure_only: self.fast_parse, + files_to_parse_afterwards: []) update_state! :processing do_or_set_failed do @@ -39,6 +41,13 @@ def parse(refresh_cache: false, structure_only: self.fast_parse) update_state! :done end + + files_to_parse_afterwards.each do |path| + ontology_version_options = OntologyVersionOptions.new(path, self.user, + do_not_parse: false) + version = OntologySaver.new(repository). + save_ontology(commit_oid, ontology_version_options) + end end # generate XML by passing the raw ontology to Hets @@ -48,12 +57,12 @@ def generate_xml(structure_only: false) [:all_is_well, input_io] end - def parse_full - parse(structure_only: false) + def parse_full(files_to_parse_afterwards = []) + parse(structure_only: false, files_to_parse_afterwards: files_to_parse_afterwards) end - def parse_fast - parse(structure_only: true) + def parse_fast(files_to_parse_afterwards = []) + parse(structure_only: true, files_to_parse_afterwards: files_to_parse_afterwards) end def retrieve_available_provers_for_self_and_children From cb08b6bacfcb9c0de6d66222071adbefba898097 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:19:21 +0100 Subject: [PATCH 143/240] Add method to check if a version was already parsed in the commit. --- app/models/repository/associations_and_attributes.rb | 1 + lib/ontology_saver.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/app/models/repository/associations_and_attributes.rb b/app/models/repository/associations_and_attributes.rb index 1ad959a00..1d5edf18f 100644 --- a/app/models/repository/associations_and_attributes.rb +++ b/app/models/repository/associations_and_attributes.rb @@ -3,6 +3,7 @@ module Repository::AssociationsAndAttributes included do has_many :ontologies, dependent: :destroy + has_many :ontology_versions, through: :ontologies has_many :url_maps, dependent: :destroy has_many :commits, dependent: :destroy has_many :access_tokens, dependent: :destroy diff --git a/lib/ontology_saver.rb b/lib/ontology_saver.rb index 0eab87049..7b10e04c0 100644 --- a/lib/ontology_saver.rb +++ b/lib/ontology_saver.rb @@ -73,6 +73,14 @@ def mark_ontology_as_having_file(path, has_file: false) protected + def already_updated_in_commit?(commit_oid, ontology_version_options) + basepath = File.basepath(ontology_version_options.filepath) + file_extension = File.extname(ontology_version_options.filepath) + repository.ontology_versions.where(commit_oid: commit_oid, + basepath: basepath, + file_extension: file_extension).any? + end + def find_or_create_ontology(ontology_version_options) ontology = find_existing_ontology(ontology_version_options) From aeee7f45fea603773fa9aa176ca54683af4c08bc Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:22:11 +0100 Subject: [PATCH 144/240] Determine the files that import the current one. --- lib/ontology_saver.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ontology_saver.rb b/lib/ontology_saver.rb index 7b10e04c0..565389aa7 100644 --- a/lib/ontology_saver.rb +++ b/lib/ontology_saver.rb @@ -157,6 +157,11 @@ def schedule_batch_parsing(versions, priority_mode: false) end end + # Files that import the current one must be parsed as well. + def files_to_parse(ontology, changed_files) + ontology.imported_by.map(&:path) - [*changed_files, ontology.path] + end + def generate_locid(basepath) "/#{repository.path}/#{basepath}" end From 89a44b1ca5aa6007b102831cc4a4e306fd32507b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:23:13 +0100 Subject: [PATCH 145/240] Handle files that need to be parsed with the current one. --- app/models/ontology/import_mappings.rb | 4 ++-- lib/ontology_saver.rb | 28 ++++++++++++++++---------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index 5c6c30ecc..f11f64fbf 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -11,8 +11,8 @@ def is_imported_from_other_repository? import_mappings_from_other_repositories.present? end - def imported_by - import_mappings.map(&:source) + def mapping_targets + source_mappings.map(&:target) end def imported_ontologies diff --git a/lib/ontology_saver.rb b/lib/ontology_saver.rb index 565389aa7..76b7c7105 100644 --- a/lib/ontology_saver.rb +++ b/lib/ontology_saver.rb @@ -7,17 +7,19 @@ def initialize(repository) self.repository = repository end - def save_ontology(commit_oid, ontology_version_options) - # we expect that this method is only called, when the ontology is 'present' - return unless Ontology.file_extensions.include?( - File.extname(ontology_version_options.filepath)) - + def save_ontology(commit_oid, ontology_version_options, changed_files: []) + # We expect that this method is only called, when we can expect an ontology + # in this file. + file_extension = File.extname(ontology_version_options.filepath) + return unless Ontology.file_extensions.include?(file_extension) + return if already_updated_in_commit?(commit_oid, ontology_version_options) ontology = find_or_create_ontology(ontology_version_options) return unless repository.master_file?(ontology, ontology_version_options) return if ontology.versions.find_by_commit_oid(commit_oid) - version = create_version(ontology, commit_oid, ontology_version_options) + version = create_version(ontology, commit_oid, ontology_version_options, + changed_files) ontology.present = true ontology.save! @@ -31,7 +33,8 @@ def suspended_save_ontologies(options={}) repository.walk_commits(options) { |commit| commits_count += 1 current_file_count = 0 - repository.git.changed_files(commit.oid).each { |f| + changed_files = repository.git.changed_files(commit.oid) + changed_files.each { |f| current_file_count += 1 if f.added? || f.modified? mark_ontology_as_having_file(f.path, has_file: true) @@ -40,7 +43,8 @@ def suspended_save_ontologies(options={}) options.delete(:user), fast_parse: repository.has_changed?(f.path, commit.oid), do_not_parse: true) - versions << save_ontology(commit.oid, ontology_version_options) + versions << save_ontology(commit.oid, ontology_version_options, + changed_files: changed_files) elsif f.renamed? ontology_version_options = OntologyVersionOptions.new( f.path, @@ -48,7 +52,8 @@ def suspended_save_ontologies(options={}) fast_parse: repository.has_changed?(f.path, commit.oid), do_not_parse: true, previous_filepath: f.delta.old_file[:path]) - versions << save_ontology(commit.oid, ontology_version_options) + versions << save_ontology(commit.oid, ontology_version_options, + changed_files: changed_files) elsif f.deleted? mark_ontology_as_having_file(f.path, has_file: false) end @@ -119,7 +124,7 @@ def corresponding_ontology_klass(filepath) is_distributed ? DistributedOntology : SingleOntology end - def create_version(ontology, commit_oid, ontology_version_options) + def create_version(ontology, commit_oid, ontology_version_options, changed_files) version = ontology.versions.build( { commit_oid: commit_oid, user: ontology_version_options.user, @@ -130,6 +135,7 @@ def create_version(ontology, commit_oid, ontology_version_options) fast_parse: ontology_version_options.fast_parse }, { without_protection: true }) version.do_not_parse! if ontology_version_options.do_not_parse + version.files_to_parse_afterwards = files_to_parse(ontology, changed_files) version.save! ontology.ontology_version = version ontology.save! @@ -159,7 +165,7 @@ def schedule_batch_parsing(versions, priority_mode: false) # Files that import the current one must be parsed as well. def files_to_parse(ontology, changed_files) - ontology.imported_by.map(&:path) - [*changed_files, ontology.path] + ontology.mapping_targets.map(&:path) - [*changed_files, ontology.path] end def generate_locid(basepath) From 1fefe380ff209b11ae14e3466a93574f7f26d595 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:35:21 +0100 Subject: [PATCH 146/240] Add hiararchical imports to seeds. --- db/seeds/040-git.rb | 2 ++ spec/fixtures/ontologies/clif/hierarchical_import1.clif | 3 +++ spec/fixtures/ontologies/clif/hierarchical_import2.clif | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 spec/fixtures/ontologies/clif/hierarchical_import1.clif create mode 100644 spec/fixtures/ontologies/clif/hierarchical_import2.clif diff --git a/db/seeds/040-git.rb b/db/seeds/040-git.rb index d7c191f0b..e1887cf3e 100644 --- a/db/seeds/040-git.rb +++ b/db/seeds/040-git.rb @@ -20,6 +20,8 @@ casl/test1.casl casl/test2.casl clif/cat.clif + clif/hierarchical_import1.clif + clif/hierarchical_import2.clif owl/generations.owl owl/pizza.owl prove/Subclass.casl diff --git a/spec/fixtures/ontologies/clif/hierarchical_import1.clif b/spec/fixtures/ontologies/clif/hierarchical_import1.clif new file mode 100644 index 000000000..7162c1198 --- /dev/null +++ b/spec/fixtures/ontologies/clif/hierarchical_import1.clif @@ -0,0 +1,3 @@ +(cl-text http://localhost:3000/default/hierarchical_import1 + (cl-imports http://localhost:3000/default/cat) +) diff --git a/spec/fixtures/ontologies/clif/hierarchical_import2.clif b/spec/fixtures/ontologies/clif/hierarchical_import2.clif new file mode 100644 index 000000000..7d97de537 --- /dev/null +++ b/spec/fixtures/ontologies/clif/hierarchical_import2.clif @@ -0,0 +1,3 @@ +(cl-text http://localhost:3000/default/hierarchical_import2 + (cl-imports http://localhost:3000/default/hierarchical_import1) +) From fd84a2bf2cc0d4463d6130f6f8ecab133a627e3a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 15:44:43 +0100 Subject: [PATCH 147/240] Move ontology version parsing spec to separate file. --- spec/models/ontology_version/parsing_spec.rb | 126 ++++++++++++++++++ spec/models/ontology_version_spec.rb | 127 ------------------- 2 files changed, 126 insertions(+), 127 deletions(-) create mode 100644 spec/models/ontology_version/parsing_spec.rb diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb new file mode 100644 index 000000000..780c6db73 --- /dev/null +++ b/spec/models/ontology_version/parsing_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' + +describe 'OntologyVersion Parsing' do + let(:user) { create :user } + let(:ontology) { create :ontology, basepath: 'pizza' } + let(:ontology_version) { ontology.save_file(ontology_file('owl/pizza.owl'), 'message', user) } + + before do + # Clear Jobs + Worker.jobs.clear + stub_hets_for('owl/pizza.owl') + end + + context 'in subdirectory' do + let(:ontology) { create :ontology, basepath: 'subdir/pizza' } + let(:qualified_locid) do + "#{Hostname.url_authority}#{ontology_version.locid}" + end + + before do + ontology_version + allow_any_instance_of(Hets::ParseCaller).to receive(:call) do |iri, *_args| + throw(:iri, iri) + end + end + + it 'should use the locid-ref for calling the parse-caller' do + expect { Worker.drain }.to throw_symbol(:iri, qualified_locid) + end + end + + context 'without exception' do + let(:commit) { ontology_version.commit } + + before do + # Run Job + # binding.pry + ontology_version + Worker.drain + end + + it 'should be done' do + expect(ontology.reload.state).to eq('done') + end + + it 'should have state_updated_at' do + expect(ontology_version.state_updated_at).to_not be(nil) + end + + it 'should contain a commit' do + expect(commit).to_not be(nil) + end + + it 'should contain a commit which refers to commit_oid' do + expect(commit.commit_oid).to eq(ontology_version.commit_oid) + end + end + + context 'with url-catalog' do + let(:repository) { ontology.repository } + let!(:url_maps) { [1,2].map { create :url_map, repository: repository } } + + before do + ontology_version + Worker.drain + end + + it 'have sent a request with url-catalog' do + hets_instance = HetsInstance.choose! + expect(WebMock). + to have_requested(:get, + /#{hets_instance.uri}\/dg\/.*\?.*url-catalog=#{url_maps.join(',')}.*/) + end + end + + context 'on sidekiq shutdown' do + before do + allow(Hets).to receive(:parse_via_api).and_raise(Sidekiq::Shutdown) + ontology_version + expect { Worker.drain }.to raise_error(Sidekiq::Shutdown) + end + + it 'should reset status to pending' do + expect(ontology.reload.state).to eq('pending') + end + end + + context 'on hets error' do + before do + allow(Hets).to receive(:parse_via_api). + and_raise(Hets::HetsError, "serious error") + ontology_version + expect { Worker.drain }.to raise_error(Hets::HetsError) + end + + it 'should set status to failed' do + expect(ontology.reload.state).to eq('failed') + end + end + + context 'on failed to update state' do + let(:ontology) { create :ontology, basepath: 'pizza' } + let(:ontology_version) { ontology.save_file(ontology_file('owl/pizza.owl'), 'message', user) } + + before do + allow(Hets).to receive(:parse_via_api). + and_raise(Hets::HetsError, "first error") + allow_any_instance_of(OntologyVersion).to receive(:after_failed).and_raise('second exception') + ontology_version + expect { Worker.drain }.to raise_error(RuntimeError) + end + + it 'should set status to failed on ontology' do + expect(ontology.reload.state).to eq('failed') + end + + it 'should set state to failed' do + expect(ontology_version.reload.state).to eq('failed') + end + + it 'should contain the nested error' do + nested_error_regex = /nested exception.*second exception.*first error/im + expect(ontology_version.reload.last_error).to match(nested_error_regex) + end + end +end diff --git a/spec/models/ontology_version_spec.rb b/spec/models/ontology_version_spec.rb index f2189d86c..68cb9cbcd 100644 --- a/spec/models/ontology_version_spec.rb +++ b/spec/models/ontology_version_spec.rb @@ -23,131 +23,4 @@ expect(ontology_version.url).to match(version_url_regex) end end - - context 'Parsing' do - let(:ontology) { create :ontology, basepath: 'pizza' } - let(:ontology_version) { ontology.save_file(ontology_file('owl/pizza.owl'), 'message', user) } - - before do - # Clear Jobs - Worker.jobs.clear - stub_hets_for('owl/pizza.owl') - end - - context 'in subdirectory' do - let(:ontology) { create :ontology, basepath: 'subdir/pizza' } - let(:qualified_locid) do - "#{Hostname.url_authority}#{ontology_version.locid}" - end - - before do - ontology_version - allow_any_instance_of(Hets::ParseCaller).to receive(:call) do |iri, *_args| - throw(:iri, iri) - end - end - - it 'should use the locid-ref for calling the parse-caller' do - expect { Worker.drain }. - to throw_symbol(:iri, qualified_locid) - end - end - - context 'without exception' do - let(:commit) { ontology_version.commit } - - before do - # Run Job - # binding.pry - ontology_version - Worker.drain - end - - it 'should be done' do - expect(ontology.reload.state).to eq('done') - end - - it 'should have state_updated_at' do - expect(ontology_version.state_updated_at).to_not be(nil) - end - - it 'should contain a commit' do - expect(commit).to_not be(nil) - end - - it 'should contain a commit which refers to commit_oid' do - expect(commit.commit_oid).to eq(ontology_version.commit_oid) - end - end - - context 'with url-catalog' do - let(:repository) { ontology.repository } - let!(:url_maps) { [1,2].map { create :url_map, repository: repository } } - - before do - ontology_version - Worker.drain - end - - it 'have sent a request with url-catalog' do - hets_instance = HetsInstance.choose! - expect(WebMock). - to have_requested(:get, - /#{hets_instance.uri}\/dg\/.*\?.*url-catalog=#{url_maps.join(',')}.*/) - end - end - - context 'on sidekiq shutdown' do - before do - allow(Hets).to receive(:parse_via_api).and_raise(Sidekiq::Shutdown) - ontology_version - expect { Worker.drain }.to raise_error(Sidekiq::Shutdown) - end - - it 'should reset status to pending' do - expect(ontology.reload.state).to eq('pending') - end - end - - context 'on hets error' do - before do - allow(Hets).to receive(:parse_via_api). - and_raise(Hets::HetsError, "serious error") - ontology_version - expect { Worker.drain }.to raise_error(Hets::HetsError) - end - - it 'should set status to failed' do - expect(ontology.reload.state).to eq('failed') - end - end - - context 'on failed to update state' do - let(:ontology) { create :ontology, basepath: 'pizza' } - let(:ontology_version) { ontology.save_file(ontology_file('owl/pizza.owl'), 'message', user) } - - before do - allow(Hets).to receive(:parse_via_api). - and_raise(Hets::HetsError, "first error") - allow_any_instance_of(OntologyVersion).to receive(:after_failed).and_raise('second exception') - ontology_version - expect { Worker.drain }.to raise_error(RuntimeError) - end - - it 'should set status to failed on ontology' do - expect(ontology.reload.state).to eq('failed') - end - - it 'should set state to failed' do - expect(ontology_version.reload.state).to eq('failed') - end - - it 'should contain the nested error' do - nested_error_regex = /nested exception.*second exception.*first error/im - expect(ontology_version.reload.last_error).to match(nested_error_regex) - end - - end - - end end From 38d7f0b2c4ff0f22f4dd294e49ebfb5dfc2556eb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 21:22:26 +0100 Subject: [PATCH 148/240] Add specs on import hierarchy. --- spec/models/ontology_version/parsing_spec.rb | 92 ++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb index 780c6db73..d7f3ab942 100644 --- a/spec/models/ontology_version/parsing_spec.rb +++ b/spec/models/ontology_version/parsing_spec.rb @@ -11,6 +11,98 @@ stub_hets_for('owl/pizza.owl') end + context 'import hierarchy', sidekiq: :inline do + before do + stub_hets_for('clif/Px.clif') + stub_hets_for('clif/Qy.clif') + end + + let(:fixture) { ontology_file('clif/Px.clif') } + let(:other_fixture) { ontology_file('clif/Qy.clif') } + let(:repository) { create :repository } + + # We do not care about the content of the clif file. Only the later on added + # mappings are important. + let!(:ontology) do + repository.save_file(fixture, 'base.clif', 'add base', user).ontology + end + let!(:ontology_initial_commit_oid) { ontology.current_version.commit_oid } + let!(:importing_ontology1) do + repository.save_file(fixture, 'importing1.clif', 'add import1', user).ontology + end + let!(:importing_ontology2) do + repository.save_file(fixture, 'importing2.clif', 'add import2', user).ontology + end + let!(:viewing_ontology) do + repository.save_file(fixture, 'viewing.clif', 'add viewing', user).ontology + end + let!(:unrelated_ontology) do + repository.save_file(fixture, 'unrelated.clif', 'add unrelated', user).ontology + end + let!(:import_mapping1) do + create :import_mapping, source: ontology, target: importing_ontology1 + end + let!(:import_mapping2) do + create :import_mapping, source: importing_ontology1, target: importing_ontology2 + end + let!(:view_mapping) do + create :import_mapping, source: ontology, target: viewing_ontology + end + + before do + ontology.save_file(other_fixture, 'changing the base', user) + end + + it 'changing the commit oid of the base ontology' do + expect(ontology.reload.current_version.commit_oid). + to_not eq(ontology_initial_commit_oid) + end + + context 'directly importing ontology' do + it 'adding a version' do + expect(importing_ontology1.reload.versions.count).to eq(2) + end + + it 'has the same version as the base ontology' do + expect(importing_ontology1.reload.current_version.commit_oid). + to eq(ontology.reload.current_version.commit_oid) + end + end + + context 'distantly importing ontology' do + it 'adding a version' do + expect(importing_ontology2.reload.versions.count).to eq(2) + end + + it 'has the same version as the base ontology' do + expect(importing_ontology2.reload.current_version.commit_oid). + to eq(ontology.reload.current_version.commit_oid) + end + end + + context 'directly viewing ontology' do + it 'adding a version' do + expect(viewing_ontology.reload.versions.count).to eq(2) + end + + it 'has the same version as the base ontology' do + expect(viewing_ontology.reload.current_version.commit_oid). + to eq(ontology.reload.current_version.commit_oid) + end + end + + context 'unrelated ontology' do + it 'not adding a version' do + expect(unrelated_ontology.reload.versions.count).to eq(1) + end + + it 'has a different version than the base ontology' do + expect(unrelated_ontology.reload.current_version.commit_oid). + to_not eq(ontology.reload.current_version.commit_oid) + end + end + end + context 'in subdirectory' do let(:ontology) { create :ontology, basepath: 'subdir/pizza' } let(:qualified_locid) do From 5d41c4207ea6d138674fab73cb982f8465e4338e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 28 Sep 2015 17:47:09 +0200 Subject: [PATCH 149/240] Fix stubs for PipelineGenerator The PipelineGenerator always tried to call Hets on incrementing ports. This could not work. Here, we fix the port to 8000 for the specs. --- spec/lib/axiom_selection_proof_status_spec.rb | 2 +- spec/lib/proof_status_determining_spec.rb | 2 +- spec/support/common_helper_methods.rb | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/lib/axiom_selection_proof_status_spec.rb b/spec/lib/axiom_selection_proof_status_spec.rb index b91495399..585313f61 100644 --- a/spec/lib/axiom_selection_proof_status_spec.rb +++ b/spec/lib/axiom_selection_proof_status_spec.rb @@ -2,7 +2,7 @@ describe 'Axiom Selection: Proof Status', :http_interaction do setup_hets - stub_fqdn_and_port_for_pipeline_generator + setup_pipeline_generator let(:generator) do FixturesGeneration::PipelineGenerator.new end diff --git a/spec/lib/proof_status_determining_spec.rb b/spec/lib/proof_status_determining_spec.rb index 8830685ea..cd9fdae5a 100644 --- a/spec/lib/proof_status_determining_spec.rb +++ b/spec/lib/proof_status_determining_spec.rb @@ -2,7 +2,7 @@ describe 'Proof Status Determining', :http_interaction do setup_hets - stub_fqdn_and_port_for_pipeline_generator + setup_pipeline_generator let(:generator) do FixturesGeneration::PipelineGenerator.new end diff --git a/spec/support/common_helper_methods.rb b/spec/support/common_helper_methods.rb index 3f45f0c68..9da4900ca 100644 --- a/spec/support/common_helper_methods.rb +++ b/spec/support/common_helper_methods.rb @@ -45,6 +45,11 @@ def version_for_file(repository, path) version = repository.save_file path, basename, "#{basename} added", dummy_user end +def setup_pipeline_generator + stub_fqdn_and_port_for_pipeline_generator + stub_hets_instance_url_for_pipeline_generator +end + def stub_fqdn_and_port_for_pipeline_generator before do fqdn = FixturesGeneration::PipelineGenerator::RAILS_SERVER_TEST_FQDN @@ -55,6 +60,13 @@ def stub_fqdn_and_port_for_pipeline_generator end end +def stub_hets_instance_url_for_pipeline_generator + before do + allow_any_instance_of(HetsInstance).to receive(:uri). + and_return('http://localhost:8000') + end +end + def schema_for(name) "https://masterthesis.rightsrestricted.com/ontohub/#{name}.json" end From 05645eeb63b34810c7626b75215de5172ae65bc2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 28 Sep 2015 17:47:52 +0200 Subject: [PATCH 150/240] Add specs on input type passing. --- spec/models/ontology_version/parsing_spec.rb | 51 ++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb index d7f3ab942..4965f301e 100644 --- a/spec/models/ontology_version/parsing_spec.rb +++ b/spec/models/ontology_version/parsing_spec.rb @@ -215,4 +215,55 @@ expect(ontology_version.reload.last_error).to match(nested_error_regex) end end + + context 'input-type parameter' do + context 'owl' do + let(:ontology_path) { 'owl/pizza.owl' } + let(:ontology) { create :ontology, basepath: 'pizza' } + let(:ontology_version) do + ontology.save_file(ontology_file(ontology_path), 'message', user) + end + + before do + # Clear Jobs + Worker.jobs.clear + stub_hets_for(ontology_path) + ontology_version + Worker.drain + end + + it '- have sent a request with input-type' do + hets_instance = HetsInstance.choose! + expect(WebMock). + to have_requested(:get, + /#{hets_instance.uri}\/dg\/.*\?.*input-type=owl.*/) + end + end + + context 'p (tptp)' do + let(:ontology_path) { 'tptp/zfmisc_1__t92_zfmisc_1.p' } + let(:ontology) do + create :ontology, + basepath: 'zfmisc_1__t92_zfmisc_1', file_extension: '.p' + end + let(:ontology_version) do + ontology.save_file(ontology_file(ontology_path), 'message', user) + end + + before do + # Clear Jobs + Worker.jobs.clear + stub_hets_for(ontology_path) + ontology_version + Worker.drain + end + + it '- have sent a request with input-type' do + hets_instance = HetsInstance.choose! + expect(WebMock). + to have_requested(:get, + /#{hets_instance.uri}\/dg\/.*\?.*input-type=tptp.*/) + end + end + end end From cc228d5a4ac3427b07171a3adab98b0f099a11b0 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 28 Sep 2015 17:48:13 +0200 Subject: [PATCH 151/240] Pass input type to Hets. --- app/models/ontology/hets_options.rb | 15 ++++++++++++--- .../fixtures_generation/direct_hets_generator.rb | 9 ++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/models/ontology/hets_options.rb b/app/models/ontology/hets_options.rb index 539658b0c..75f32688b 100644 --- a/app/models/ontology/hets_options.rb +++ b/app/models/ontology/hets_options.rb @@ -3,12 +3,21 @@ module Ontology::HetsOptions # Hets has some trouble inferring those input types # by itself, so we give it a hint: - EXTENSIONS_TO_INPUT_TYPES = {'.tptp' => 'tptp', - '.p' => 'tptp'} + EXTENSIONS_TO_INPUT_TYPES = {'.p' => 'tptp'} def hets_options Hets::HetsOptions.new(:'url-catalog' => repository.url_maps, :'access-token' => repository.generate_access_token, - :'input-type' => EXTENSIONS_TO_INPUT_TYPES[file_extension]) + :'input-type' => input_type) + end + + protected + + def input_type + EXTENSIONS_TO_INPUT_TYPES[file_extension] || file_extension_without_dot + end + + def file_extension_without_dot + file_extension[1..-1] if file_extension end end diff --git a/spec/support/fixtures_generation/direct_hets_generator.rb b/spec/support/fixtures_generation/direct_hets_generator.rb index a862d20f3..6996c64f5 100644 --- a/spec/support/fixtures_generation/direct_hets_generator.rb +++ b/spec/support/fixtures_generation/direct_hets_generator.rb @@ -47,11 +47,10 @@ def http_request(method, uri, header, data) def input_type(file) extension = File.extname(file) - if type = Ontology::HetsOptions::EXTENSIONS_TO_INPUT_TYPES[extension] - "input-type=#{type};" - else - '' - end + type = + Ontology::HetsOptions::EXTENSIONS_TO_INPUT_TYPES[extension] || + extension[1..-1] + type ? "input-type=#{type};" : '' end end end From 72d2bd248ed7ee4b30e31e9203ad6136a5e18c30 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 28 Sep 2015 17:48:51 +0200 Subject: [PATCH 152/240] Update VCR cassettes for proving specs. --- ...xiom_Selection_Proof_Status_all_axioms.yml | 54 +++++++++++++++++++ ...Status_no_axioms_meaning_all_are_used_.yml | 54 +++++++++++++++++++ ...ion_Proof_Status_not_sufficient_axioms.yml | 54 +++++++++++++++++++ ...ology_CounterSatisfiable_after_proving.yml | 54 +++++++++++++++++++ ...PASS_on_ontology_Theorem_after_proving.yml | 54 +++++++++++++++++++ ...ology_CounterSatisfiable_after_proving.yml | 54 +++++++++++++++++++ ...over_on_ontology_Theorem_after_proving.yml | 54 +++++++++++++++++++ 7 files changed, 378 insertions(+) diff --git a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_all_axioms.yml b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_all_axioms.yml index 14caa00ff..1072b6dc5 100644 --- a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_all_axioms.yml +++ b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_all_axioms.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:21:31 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-1%2FSimple_Implications%2F%2FGroup/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"SPASS","timeout":"9","axioms":["Ax2","ga_assoc___+__","leftunit"],"input-type":"casl","theorems":["rightunit"],"node":"Group"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:43:13 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "Group", + "goals": [{ + "name": "rightunit", + "result": "Proved", + "details": "", + "used_prover": { "identifier": "SPASS", "name": "SPASS" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { + "time_limit": 9, "extra_options": [ "-DocProof"] }, + "proof_tree": "", + "used_time": { + "seconds": 0, "components": { "hours": 0, "mins": 0, "secs": 0 } + }, + "used_axioms": [ "leftunit", "Ax2", "ga_assoc___+__"], + "prover_output": + "\n--------------------------SPASS-START-----------------------------\nInput Problem:\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n This is a unit equality problem.\n This is a problem that has, if any, a non-trivial domain model.\n The conjecture is ground.\n Axiom clauses: 3 Conjecture clauses: 1\n Inferences: IEqR=1 ISpR=1 ISpL=1 \n Reductions: RFRew=1 RBRew=1 RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RFSub=1 RBSub=1 \n Extras : Input Saturation, No Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1\n Precedence: o__Plus__ > minus__ > skc1 > skc0 > zero\n Ordering : KBO\nProcessed Problem:\n\nWorked Off Clauses:\n\nUsable Clauses:\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n\tGiven clause: 1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n\tGiven clause: 2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n\tGiven clause: 3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n\tGiven clause: 4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n\tGiven clause: 11[0:SpR:1.0,4.0] || -> equal(o__Plus__(U,o__Plus__(zero,V)),o__Plus__(U,V))**.\n\tGiven clause: 21[0:Rew:1.0,20.0] || -> equal(o__Plus__(U,minus__(zero)),U)**.\n\tGiven clause: 8[0:SpR:4.0,3.0] || -> equal(o__Plus__(U,o__Plus__(V,minus__(o__Plus__(U,V)))),zero)**.\n\tGiven clause: 12[0:SpR:3.0,4.0] || -> equal(o__Plus__(U,o__Plus__(minus__(U),V)),o__Plus__(zero,V))**.\n\tGiven clause: 67[0:Rew:1.0,61.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),U)**.\nSPASS V 3.7 \nSPASS beiseite: Proof found.\nProblem: Read from stdin. \nSPASS derived 48 clauses, backtracked 0 clauses, performed 0 splits and kept 17 clauses.\nSPASS allocated 46026 KBytes.\nSPASS spent\t0:00:00.03 on the problem.\n\t\t0:00:00.01 for the input.\n\t\t0:00:00.01 for the FLOTTER CNF translation.\n\t\t0:00:00.00 for inferences.\n\t\t0:00:00.00 for the backtracking.\n\t\t0:00:00.00 for the reduction.\n\n\nHere is a proof with depth 3, length 11 :\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n11[0:SpR:1.0,4.0] || -> equal(o__Plus__(U,o__Plus__(zero,V)),o__Plus__(U,V))**.\n12[0:SpR:3.0,4.0] || -> equal(o__Plus__(U,o__Plus__(minus__(U),V)),o__Plus__(zero,V))**.\n61[0:SpR:3.0,12.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),o__Plus__(U,zero))**.\n67[0:Rew:1.0,61.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),U)**.\n77[0:SpR:67.0,11.0] || -> equal(o__Plus__(U,minus__(minus__(V))),o__Plus__(U,V))**.\n79[0:Rew:77.0,67.0] || -> equal(o__Plus__(zero,U),U)**.\n80[0:UnC:79.0,2.0] || -> .\nFormulae used in the proof : leftunit rightunit ax2 ga_assoc___Plus__\n\n--------------------------SPASS-STOP------------------------------\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:43:13 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_no_axioms_meaning_all_are_used_.yml b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_no_axioms_meaning_all_are_used_.yml index 80520d449..fa3700853 100644 --- a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_no_axioms_meaning_all_are_used_.yml +++ b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_no_axioms_meaning_all_are_used_.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:21:29 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-2%2FSimple_Implications%2F%2FGroup/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"SPASS","timeout":"5","input-type":"casl","theorems":["rightunit"],"node":"Group"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:44:50 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "Group", + "goals": [{ + "name": "rightunit", + "result": "Proved", + "details": "", + "used_prover": { "identifier": "SPASS", "name": "SPASS" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { + "time_limit": 5, "extra_options": [ "-DocProof"] }, + "proof_tree": "", + "used_time": { + "seconds": 0, "components": { "hours": 0, "mins": 0, "secs": 0 } + }, + "used_axioms": [ "leftunit", "Ax2", "ga_assoc___+__"], + "prover_output": + "\n--------------------------SPASS-START-----------------------------\nInput Problem:\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n This is a unit equality problem.\n This is a problem that has, if any, a non-trivial domain model.\n The conjecture is ground.\n Axiom clauses: 3 Conjecture clauses: 1\n Inferences: IEqR=1 ISpR=1 ISpL=1 \n Reductions: RFRew=1 RBRew=1 RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RFSub=1 RBSub=1 \n Extras : Input Saturation, No Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1\n Precedence: o__Plus__ > minus__ > skc1 > skc0 > zero\n Ordering : KBO\nProcessed Problem:\n\nWorked Off Clauses:\n\nUsable Clauses:\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n\tGiven clause: 1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n\tGiven clause: 2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n\tGiven clause: 3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n\tGiven clause: 4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n\tGiven clause: 11[0:SpR:1.0,4.0] || -> equal(o__Plus__(U,o__Plus__(zero,V)),o__Plus__(U,V))**.\n\tGiven clause: 21[0:Rew:1.0,20.0] || -> equal(o__Plus__(U,minus__(zero)),U)**.\n\tGiven clause: 8[0:SpR:4.0,3.0] || -> equal(o__Plus__(U,o__Plus__(V,minus__(o__Plus__(U,V)))),zero)**.\n\tGiven clause: 12[0:SpR:3.0,4.0] || -> equal(o__Plus__(U,o__Plus__(minus__(U),V)),o__Plus__(zero,V))**.\n\tGiven clause: 67[0:Rew:1.0,61.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),U)**.\nSPASS V 3.7 \nSPASS beiseite: Proof found.\nProblem: Read from stdin. \nSPASS derived 48 clauses, backtracked 0 clauses, performed 0 splits and kept 17 clauses.\nSPASS allocated 46026 KBytes.\nSPASS spent\t0:00:00.03 on the problem.\n\t\t0:00:00.01 for the input.\n\t\t0:00:00.01 for the FLOTTER CNF translation.\n\t\t0:00:00.00 for inferences.\n\t\t0:00:00.00 for the backtracking.\n\t\t0:00:00.00 for the reduction.\n\n\nHere is a proof with depth 3, length 11 :\n1[0:Inp] || -> equal(o__Plus__(U,zero),U)**.\n2[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n3[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n4[0:Inp] || -> equal(o__Plus__(o__Plus__(U,V),W),o__Plus__(U,o__Plus__(V,W)))**.\n11[0:SpR:1.0,4.0] || -> equal(o__Plus__(U,o__Plus__(zero,V)),o__Plus__(U,V))**.\n12[0:SpR:3.0,4.0] || -> equal(o__Plus__(U,o__Plus__(minus__(U),V)),o__Plus__(zero,V))**.\n61[0:SpR:3.0,12.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),o__Plus__(U,zero))**.\n67[0:Rew:1.0,61.0] || -> equal(o__Plus__(zero,minus__(minus__(U))),U)**.\n77[0:SpR:67.0,11.0] || -> equal(o__Plus__(U,minus__(minus__(V))),o__Plus__(U,V))**.\n79[0:Rew:77.0,67.0] || -> equal(o__Plus__(zero,U),U)**.\n80[0:UnC:79.0,2.0] || -> .\nFormulae used in the proof : leftunit rightunit ax2 ga_assoc___Plus__\n\n--------------------------SPASS-STOP------------------------------\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:44:50 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_not_sufficient_axioms.yml b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_not_sufficient_axioms.yml index 19c170b18..3f5bf0d3e 100644 --- a/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_not_sufficient_axioms.yml +++ b/spec/fixtures/vcr/specs/lib/axiom_selection_proof_status_spec/Axiom_Selection_Proof_Status_not_sufficient_axioms.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:21:27 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-3%2FSimple_Implications%2F%2FGroup/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"SPASS","timeout":"9","axioms":["Ax2"],"input-type":"casl","theorems":["rightunit"],"node":"Group"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:44:53 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "Group", + "goals": [{ + "name": "rightunit", + "result": "Disproved", + "details": "", + "used_prover": { "identifier": "SPASS", "name": "SPASS" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { + "time_limit": 9, "extra_options": [ "-DocProof"] }, + "proof_tree": "", + "used_time": { + "seconds": 0, "components": { "hours": 0, "mins": 0, "secs": 0 } + }, + "used_axioms": [], + "prover_output": + "\n--------------------------SPASS-START-----------------------------\nInput Problem:\n1[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n2[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n This is a unit equality problem.\n This is a problem that has, if any, a non-trivial domain model.\n The conjecture is ground.\n Axiom clauses: 1 Conjecture clauses: 1\n Inferences: IEqR=1 ISpR=1 ISpL=1 \n Reductions: RFRew=1 RBRew=1 RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RFSub=1 RBSub=1 \n Extras : Input Saturation, No Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1\n Precedence: o__Plus__ > minus__ > skc1 > skc0 > zero\n Ordering : KBO\nProcessed Problem:\n\nWorked Off Clauses:\n\nUsable Clauses:\n1[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n2[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n\tGiven clause: 1[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n\tGiven clause: 2[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\nSPASS V 3.7 \nSPASS beiseite: Completion found.\nProblem: Read from stdin. \nSPASS derived 1 clauses, backtracked 0 clauses, performed 0 splits and kept 2 clauses.\nSPASS allocated 45934 KBytes.\nSPASS spent\t0:00:00.03 on the problem.\n\t\t0:00:00.01 for the input.\n\t\t0:00:00.01 for the FLOTTER CNF translation.\n\t\t0:00:00.00 for inferences.\n\t\t0:00:00.00 for the backtracking.\n\t\t0:00:00.00 for the reduction.\n\n\n The saturated set of worked-off clauses is :\n2[0:Inp] || -> equal(o__Plus__(U,minus__(U)),zero)**.\n1[0:Inp] || equal(o__Plus__(zero,skc1),skc1)** -> .\n--------------------------SPASS-STOP------------------------------\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:44:53 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_CounterSatisfiable_after_proving.yml b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_CounterSatisfiable_after_proving.yml index 8251edf5a..560cb5365 100644 --- a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_CounterSatisfiable_after_proving.yml +++ b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_CounterSatisfiable_after_proving.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:09:51 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-6%2FSimple_Implications%2F%2FCounterSatisfiable/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"SPASS","timeout":"8","input-type":"casl","theorems":["Ax2"],"node":"CounterSatisfiable"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:45:16 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "CounterSatisfiable", + "goals": [{ + "name": "Ax2", + "result": "Disproved", + "details": "", + "used_prover": { "identifier": "SPASS", "name": "SPASS" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { + "time_limit": 8, "extra_options": [ "-DocProof"] }, + "proof_tree": "", + "used_time": { + "seconds": 0, "components": { "hours": 0, "mins": 0, "secs": 0 } + }, + "used_axioms": [], + "prover_output": + "\n--------------------------SPASS-START-----------------------------\nInput Problem:\n1[0:Inp] || -> equal(zero,one)**.\n2[0:Inp] || -> equal(U,zero)*.\n This is a unit equality problem.\n This is a problem that has, if any, a finite domain model.\n There is a finite domain clause.\n There are no function symbols.\n The conjecture is ground.\n Axiom clauses: 1 Conjecture clauses: 1\n Inferences: ISpR=1 ISpL=1 \n Reductions: RFRew=1 RBRew=1 RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RFSub=1 RBSub=1 \n Extras : Input Saturation, Always Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1\n Precedence: zero > one\n Ordering : KBO\nProcessed Problem:\n\nWorked Off Clauses:\n\nUsable Clauses:\n3[0:Rew:1.0,2.0] || -> equal(U,one)*.\n\tGiven clause: 3[0:Rew:1.0,2.0] || -> equal(U,one)*.\n\tGiven clause: 5[0:SpR:3.0,3.0] || -> equal(U,V)*.\nSPASS V 3.7 \nSPASS beiseite: Completion found.\nProblem: Read from stdin. \nSPASS derived 3 clauses, backtracked 0 clauses, performed 0 splits and kept 4 clauses.\nSPASS allocated 45932 KBytes.\nSPASS spent\t0:00:00.02 on the problem.\n\t\t0:00:00.01 for the input.\n\t\t0:00:00.01 for the FLOTTER CNF translation.\n\t\t0:00:00.00 for inferences.\n\t\t0:00:00.00 for the backtracking.\n\t\t0:00:00.00 for the reduction.\n\n\n The saturated set of worked-off clauses is :\n5[0:SpR:3.0,3.0] || -> equal(U,V)*.\n--------------------------SPASS-STOP------------------------------\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:45:16 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_Theorem_after_proving.yml b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_Theorem_after_proving.yml index 572a2a95d..676cf1ded 100644 --- a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_Theorem_after_proving.yml +++ b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_SPASS_on_ontology_Theorem_after_proving.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:09:53 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-7%2FSimple_Implications%2F%2FTheorem/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"SPASS","timeout":"8","input-type":"casl","theorems":["Ax1"],"node":"Theorem"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:45:18 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "Theorem", + "goals": [{ + "name": "Ax1", + "result": "Proved", + "details": "", + "used_prover": { "identifier": "SPASS", "name": "SPASS" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { + "time_limit": 8, "extra_options": [ "-DocProof"] }, + "proof_tree": "", + "used_time": { + "seconds": 0, "components": { "hours": 0, "mins": 0, "secs": 0 } + }, + "used_axioms": [], + "prover_output": + "\n--------------------------SPASS-START-----------------------------\nInput Problem:\n1[0:Inp] || equal(skc1,skc1)* -> .\n This is a unit equality problem.\n This is a problem that has, if any, a finite domain model.\n There are no function symbols.\n The conjecture is ground.\n Axiom clauses: 0 Conjecture clauses: 1\n Inferences: IEqR=1 \n Reductions: RFMRR=1 RBMRR=1 RObv=1 RUnC=1 RTaut=1 RFSub=1 RBSub=1 \n Extras : Input Saturation, Always Selection, No Splitting, Full Reduction, Ratio: 5, FuncWeight: 1, VarWeight: 1\n Precedence: zero > skc0 > skc1\n Ordering : KBO\nProcessed Problem:\n\nWorked Off Clauses:\n\nUsable Clauses:\n\nSPASS V 3.7 \nSPASS beiseite: Proof found.\nProblem: Read from stdin. \nSPASS derived 0 clauses, backtracked 0 clauses, performed 0 splits and kept 1 clauses.\nSPASS allocated 45930 KBytes.\nSPASS spent\t0:00:00.03 on the problem.\n\t\t0:00:00.01 for the input.\n\t\t0:00:00.01 for the FLOTTER CNF translation.\n\t\t0:00:00.00 for inferences.\n\t\t0:00:00.00 for the backtracking.\n\t\t0:00:00.00 for the reduction.\n\n\nHere is a proof with depth 0, length 2 :\n1[0:Inp] || equal(skc1,skc1)* -> .\n2[0:Obv:1.0] || -> .\nFormulae used in the proof : ax1\n\n--------------------------SPASS-STOP------------------------------\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:45:18 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_CounterSatisfiable_after_proving.yml b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_CounterSatisfiable_after_proving.yml index 10bab0dea..6f981bf2d 100644 --- a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_CounterSatisfiable_after_proving.yml +++ b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_CounterSatisfiable_after_proving.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:09:31 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-2%2FSimple_Implications%2F%2FCounterSatisfiable/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"eprover","timeout":"8","input-type":"casl","theorems":["Ax2"],"node":"CounterSatisfiable"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:45:08 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "CounterSatisfiable", + "goals": [{ + "name": "Ax2", + "result": "Disproved", + "details": "", + "used_prover": { "identifier": "eprover", "name": "eprover" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { "time_limit": 8, "extra_options": [] }, + "proof_tree": "", + "used_time": { + "seconds": -1, + "components": { "hours": -1, "mins": 59, "secs": 59 } + }, + "used_axioms": [], + "prover_output": + "# Parsed axioms : 2\n# SZS output end Saturation.\nfof(c_0_1, axiom, (![X1]:X1=zero), c_0_0).\nfof(c_0_0, axiom, (![X1]:X1=zero), file('/var/folders/h0/h5fs6h4n4wg1qpj05yd205dc0000gn/T/_ax240647.tptp', ax1)).\n# SZS output start Saturation.\n# SZS status CounterSatisfiable\n# Scanning for AC axioms\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:45:08 GMT recorded_with: VCR 2.9.3 diff --git a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_Theorem_after_proving.yml b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_Theorem_after_proving.yml index 1e62d6287..9f47d2838 100644 --- a/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_Theorem_after_proving.yml +++ b/spec/fixtures/vcr/specs/lib/proof_status_determining_spec/Proof_Status_Determining_with_eprover_on_ontology_Theorem_after_proving.yml @@ -54,4 +54,58 @@ http_interactions: }] http_version: recorded_at: Tue, 14 Jul 2015 06:09:49 GMT +- request: + method: post + uri: http://localhost:8000/prove/http:%2F%2Flocalhost:3001%2Fref%2F1%2Frepository-5%2FSimple_Implications%2F%2FTheorem/auto?input-type=casl%3B + body: + encoding: UTF-8 + string: '{"format":"json","include":"true","prover":"eprover","timeout":"5","input-type":"casl","theorems":["Ax1"],"node":"Theorem"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost:8000 + response: + status: + code: 200 + message: OK + headers: + Transfer-Encoding: + - chunked + Date: + - Mon, 28 Sep 2015 15:45:14 GMT + Server: + - Warp/3.0.1.1 + Content-Type: + - application/json + body: + encoding: UTF-8 + string: |- + [{ + "node": "Theorem", + "goals": [{ + "name": "Ax1", + "result": "Proved", + "details": "", + "used_prover": { "identifier": "eprover", "name": "eprover" }, + "used_translation": "CASL2SoftFOL", + "tactic_script": { "time_limit": 5, "extra_options": [] }, + "proof_tree": "", + "used_time": { + "seconds": -1, + "components": { "hours": -1, "mins": 59, "secs": 59 } + }, + "used_axioms": [], + "prover_output": + "# Parsed axioms : 1\n# SZS output end CNFRefutation.\n# SZS output start CNFRefutation.\n# SZS status Theorem\n" + }] + }] + http_version: + recorded_at: Mon, 28 Sep 2015 15:45:14 GMT recorded_with: VCR 2.9.3 From 794ce9de30f44a89d6063f683d2f32677255da52 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 19 Oct 2015 10:16:59 +0200 Subject: [PATCH 153/240] Remove semicolon at the end of the URL. --- spec/support/fixtures_generation/direct_hets_generator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/support/fixtures_generation/direct_hets_generator.rb b/spec/support/fixtures_generation/direct_hets_generator.rb index 6996c64f5..e324b6d70 100644 --- a/spec/support/fixtures_generation/direct_hets_generator.rb +++ b/spec/support/fixtures_generation/direct_hets_generator.rb @@ -24,7 +24,7 @@ def call_hets(file, command, hets_iri = "#{HETS_BASE_IRI}/#{command}/#{escaped_iri}" hets_iri << hets_api_options hets_iri << "?#{input_type(file)}" - hets_iri << query_string + hets_iri << ";#{query_string}" if query_string FileUtils.rm_f(recorded_file(file)) VCR.use_cassette(cassette_path_in_fixtures(file)) do @@ -50,7 +50,7 @@ def input_type(file) type = Ontology::HetsOptions::EXTENSIONS_TO_INPUT_TYPES[extension] || extension[1..-1] - type ? "input-type=#{type};" : '' + type ? "input-type=#{type}" : '' end end end From 0aa27666f17b46d0dea225ad98352b99a81b3a58 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 15 Jan 2016 17:38:52 +0100 Subject: [PATCH 154/240] Update hound config to current rubocop version. --- .hound.yml | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/.hound.yml b/.hound.yml index 47f8c2929..031fc714a 100644 --- a/.hound.yml +++ b/.hound.yml @@ -75,7 +75,7 @@ Style/BlockEndNewline: Description: 'Put end statement of multiline block on its own line.' Enabled: true -Style/Blocks: +Style/BlockDelimiters: Description: >- Avoid using {...} for multi-line blocks (multiline chaining is always ugly). @@ -183,8 +183,20 @@ Style/EmptyLinesAroundAccessModifier: Description: "Keep blank lines around access modifiers." Enabled: false -Style/EmptyLinesAroundBody: - Description: "Keeps track of empty lines around expression bodies." +Style/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + Enabled: true + +Style/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around class bodies." + Enabled: true + +Style/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around module bodies." + Enabled: true + +Style/EmptyLinesAroundMethodBody: + Description: "Keeps track of empty lines around method bodies." Enabled: true Style/EmptyLiteral: @@ -447,7 +459,7 @@ Style/SingleLineMethods: Description: 'Avoid single-line methods.' Enabled: true -Style/SingleSpaceBeforeFirstArg: +Style/SpaceBeforeFirstArg: Description: >- Checks that exactly one space is used between a method name and the first argument for method calls without parentheses. @@ -541,7 +553,6 @@ Style/SpecialGlobalVars: Style/StringLiterals: Description: 'Checks if uses of quotes match the configured preference.' Enabled: true - Style: 'single_quotes' EnforcedStyle: single_quotes Style/Tab: @@ -552,9 +563,14 @@ Style/TrailingBlankLines: Description: 'Checks trailing blank lines and final newline.' Enabled: true -Style/TrailingComma: +Style/TrailingCommaInArguments: + EnforcedStyleForMultiline: comma + Description: 'Checks for trailing comma in argument lists.' + Enabled: true + +Style/TrailingCommaInLiteral: EnforcedStyleForMultiline: comma - Description: 'Checks for trailing comma in parameter lists and literals.' + Description: 'Checks for trailing comma in array and hash literals.' Enabled: true Style/TrailingWhitespace: @@ -579,10 +595,6 @@ Style/UnneededPercentQ: Description: 'Checks for %q/%Q when single quotes or double quotes would do.' Enabled: true -Style/UnneededPercentX: - Description: 'Checks for %x when `` would do.' - Enabled: true - Style/VariableInterpolation: Description: >- Don't interpolate global, instance and class variables @@ -760,12 +772,6 @@ Lint/ShadowingOuterLocalVariable: for block arguments or block local variables. Enabled: true -Lint/SpaceBeforeFirstArg: - Description: >- - Put a space between a method name and the first argument - in a method call without parentheses. - Enabled: true - Lint/StringConversionInInterpolation: Description: 'Checks for Object#to_s usage in string interpolation.' Enabled: true @@ -816,10 +822,6 @@ Rails/ActionFilter: Description: 'Enforces consistent use of action filter methods.' Enabled: false -Rails/DefaultScope: - Description: 'Checks if the argument passed to default_scope is a block.' - Enabled: true - Rails/Delegate: Description: 'Prefer delegate method for delegations.' Enabled: true From 2a18c4c4599ac56a28a7e38bb286b5db8b05b68c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 16 Jan 2016 18:03:13 +0100 Subject: [PATCH 155/240] Use rubocop's and hound's inheritance This allows to use rubocop as well as hound without duplicating the styleguide config file. By default, rubocop uses the project's .rubocop.yml as config file. This tremendously decreases complexity of the stylecheck script. --- .hound.yml | 848 +--------------------------------------------- .rubocop.yml | 847 +++++++++++++++++++++++++++++++++++++++++++++ script/stylecheck | 20 +- 3 files changed, 850 insertions(+), 865 deletions(-) create mode 100644 .rubocop.yml diff --git a/.hound.yml b/.hound.yml index 031fc714a..86a41a504 100644 --- a/.hound.yml +++ b/.hound.yml @@ -1,849 +1,3 @@ -AllCops: - Exclude: - - 'test/**/*' - - 'spec/**/*' - - 'features/**/*' - - 'script/**/*' - ShowCopNames: true -# These are all the cops that are enabled in the default configuration. - -Style/AccessModifierIndentation: - Description: Check indentation of private/protected visibility modifiers. - Enabled: true - -Style/AccessorMethodName: - Description: Check the naming of accessor methods for get_/set_. - Enabled: true - -Style/Alias: - Description: 'Use alias_method instead of alias.' - Enabled: true - -Style/AlignArray: - Description: >- - Align the elements of an array literal if they span more than - one line. - Enabled: true - -Style/AlignHash: - Description: >- - Align the elements of a hash literal if they span more than - one line. - Enabled: true - -Style/AlignParameters: - Description: >- - Align the parameters of a method call if they span more - than one line. - Enabled: false - -Style/AndOr: - Description: 'Use &&/|| instead of and/or.' - Enabled: true - -Style/ArrayJoin: - Description: 'Use Array#join instead of Array#*.' - Enabled: true - -Style/AsciiComments: - Description: 'Use only ascii symbols in comments.' - Enabled: true - -Style/AsciiIdentifiers: - Description: 'Use only ascii symbols in identifiers.' - Enabled: true - -Style/Attr: - Description: 'Checks for uses of Module#attr.' - Enabled: true - -Style/BeginBlock: - Description: 'Avoid the use of BEGIN blocks.' - Enabled: true - -Style/BarePercentLiterals: - Description: 'Checks if usage of %() or %Q() matches configuration.' - Enabled: true - -Style/BlockComments: - Description: 'Do not use block comments.' - Enabled: true - -Style/BlockEndNewline: - Description: 'Put end statement of multiline block on its own line.' - Enabled: true - -Style/BlockDelimiters: - Description: >- - Avoid using {...} for multi-line blocks (multiline chaining is - always ugly). - Prefer {...} over do...end for single-line blocks. - Enabled: true - -Style/BracesAroundHashParameters: - Description: 'Enforce braces style inside hash parameters.' - Enabled: true - -Style/CaseEquality: - Description: 'Avoid explicit use of the case equality operator(===).' - Enabled: true - -Style/CaseIndentation: - Description: 'Indentation of when in a case/when/[else/]end.' - Enabled: true - -Style/CharacterLiteral: - Description: 'Checks for uses of character literals.' - Enabled: true - -Style/ClassAndModuleCamelCase: - Description: 'Use CamelCase for classes and modules.' - Enabled: true - -Style/ClassAndModuleChildren: - Description: 'Checks style of children classes and modules.' - Enabled: false - -Style/ClassCheck: - Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' - Enabled: true - -Style/ClassMethods: - Description: 'Use self when defining module/class methods.' - Enabled: true - -Style/ClassVars: - Description: 'Avoid the use of class variables.' - Enabled: true - -Style/CollectionMethods: - Description: 'Preferred collection methods.' - Enabled: true - PreferredMethods: - collect: 'map' - collect!: 'map!' - inject: 'reduce' - detect: 'find' - find_all: 'select' - - -Style/ColonMethodCall: - Description: 'Do not use :: for method call.' - Enabled: true - -Style/CommentAnnotation: - Description: >- - Checks formatting of special comments - (TODO, FIXME, OPTIMIZE, HACK, REVIEW). - Enabled: true - -Style/CommentIndentation: - Description: 'Indentation of comments.' - Enabled: true - -Style/ConstantName: - Description: 'Constants should use SCREAMING_SNAKE_CASE.' - Enabled: true - -Style/DefWithParentheses: - Description: 'Use def with parentheses when there are arguments.' - Enabled: true - -Style/DeprecatedHashMethods: - Description: 'Checks for use of deprecated Hash methods.' - Enabled: true - -Style/Documentation: - Description: 'Document classes and non-namespace modules.' - Enabled: false - -Style/DotPosition: - Description: 'Checks the position of the dot in multi-line method calls.' - Enabled: true - -Style/DoubleNegation: - Description: 'Checks for uses of double negation (!!).' - Enabled: false - -Style/EachWithObject: - Description: 'Prefer `each_with_object` over `inject` or `reduce`.' - Enabled: false - -Style/EmptyLineBetweenDefs: - Description: 'Use empty lines between defs.' - Enabled: true - -Style/EmptyLines: - Description: "Don't use several empty lines in a row." - Enabled: true - -Style/EmptyLinesAroundAccessModifier: - Description: "Keep blank lines around access modifiers." - Enabled: false - -Style/EmptyLinesAroundBlockBody: - Description: "Keeps track of empty lines around block bodies." - Enabled: true - -Style/EmptyLinesAroundClassBody: - Description: "Keeps track of empty lines around class bodies." - Enabled: true - -Style/EmptyLinesAroundModuleBody: - Description: "Keeps track of empty lines around module bodies." - Enabled: true - -Style/EmptyLinesAroundMethodBody: - Description: "Keeps track of empty lines around method bodies." - Enabled: true - -Style/EmptyLiteral: - Description: 'Prefer literals to Array.new/Hash.new/String.new.' - Enabled: true - -Style/Encoding: - Description: 'Use UTF-8 as the source file encoding.' - Enabled: true - -Style/EndBlock: - Description: 'Avoid the use of END blocks.' - Enabled: true - -Style/EndOfLine: - Description: 'Use Unix-style line endings.' - Enabled: true - -Style/EvenOdd: - Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' - Enabled: true - -Style/FileName: - Description: 'Use snake_case for source file names.' - Enabled: false - Exclude: - - 'db/**/*' - -Style/FlipFlop: - Description: 'Checks for flip flops' - Enabled: true - -Style/For: - Description: 'Checks use of for or each in multiline loops.' - Enabled: true - -Style/FormatString: - Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' - Enabled: true - -Style/GlobalVars: - Description: 'Do not introduce global variables.' - Enabled: true - -Style/GuardClause: - Description: 'Check for conditionals that can be replaced with guard clauses' - Enabled: false - -Style/HashSyntax: - Description: >- - Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax - { :a => 1, :b => 2 }. - Enabled: true - -Style/IfUnlessModifier: - Description: >- - Favor modifier if/unless usage when you have a - single-line body. - Enabled: true - -Style/IfWithSemicolon: - Description: 'Never use if x; .... Use the ternary operator instead.' - Enabled: true - -Style/IndentationConsistency: - Description: 'Keep indentation straight.' - Enabled: true - -Style/IndentationWidth: - Description: 'Use 2 spaces for indentation.' - Enabled: true - -Style/IndentArray: - Description: >- - Checks the indentation of the first element in an array - literal. - Enabled: true - -Style/IndentHash: - Description: 'Checks the indentation of the first key in a hash literal.' - Enabled: false - -Style/Lambda: - Description: 'Use the new lambda literal syntax for single-line blocks.' - Enabled: false - -Style/LambdaCall: - Description: 'Use lambda.call(...) instead of lambda.(...).' - Enabled: true - -Style/LeadingCommentSpace: - Description: 'Comments should start with a space.' - Enabled: true - -Style/LineEndConcatenation: - Description: >- - Use \ instead of + or << to concatenate two string literals at - line end. - Enabled: true - -Style/MethodCallParentheses: - Description: 'Do not use parentheses for method calls with no arguments.' - Enabled: true - -Style/MethodDefParentheses: - Description: >- - Checks if the method definitions have or don't have - parentheses. - Enabled: true - -Style/MethodName: - Description: 'Use the configured style when naming methods.' - Enabled: true - -Style/ModuleFunction: - Description: 'Checks for usage of `extend self` in modules.' - Enabled: true - -Style/MultilineBlockChain: - Description: 'Avoid multi-line chains of blocks.' - Enabled: true - -Style/MultilineBlockLayout: - Description: 'Ensures newlines after multiline block do statements.' - Enabled: true - -Style/MultilineIfThen: - Description: 'Never use then for multi-line if/unless.' - Enabled: true - -Style/MultilineTernaryOperator: - Description: >- - Avoid multi-line ?: (the ternary operator); - use if/unless instead. - Enabled: true - -Style/NegatedIf: - Description: >- - Favor unless over if for negative conditions - (or control flow or). - Enabled: true - -Style/NegatedWhile: - Description: 'Favor until over while for negative conditions.' - Enabled: true - -Style/NestedTernaryOperator: - Description: 'Use one expression per branch in a ternary operator.' - Enabled: true - -Style/Next: - Description: 'Use `next` to skip iteration instead of a condition at the end.' - Enabled: true - -Style/NilComparison: - Description: 'Prefer x.nil? to x == nil.' - Enabled: true - -Style/NonNilCheck: - Description: 'Checks for redundant nil checks.' - Enabled: true - -Style/Not: - Description: 'Use ! instead of not.' - Enabled: true - -Style/NumericLiterals: - Description: >- - Add underscores to large numeric literals to improve their - readability. - Enabled: true - -Style/OneLineConditional: - Description: >- - Favor the ternary operator(?:) over - if/then/else/end constructs. - Enabled: true - -Style/OpMethod: - Description: 'When defining binary operators, name the argument other.' - Enabled: true - -Style/ParenthesesAroundCondition: - Description: >- - Don't use parentheses around the condition of an - if/unless/while. - Enabled: true - -Style/PercentLiteralDelimiters: - Description: 'Use `%`-literal delimiters consistently' - Enabled: true - -Style/PercentQLiterals: - Description: 'Checks if uses of %Q/%q match the configured preference.' - Enabled: true - -Style/PerlBackrefs: - Description: 'Avoid Perl-style regex back references.' - Enabled: true - -Style/PredicateName: - Description: 'Check the names of predicate methods.' - Enabled: true - -Style/Proc: - Description: 'Use proc instead of Proc.new.' - Enabled: true - -Style/RaiseArgs: - Description: 'Checks the arguments passed to raise/fail.' - Enabled: false - -Style/RedundantBegin: - Description: "Don't use begin blocks when they are not needed." - Enabled: true - -Style/RedundantException: - Description: "Checks for an obsolete RuntimeException argument in raise/fail." - Enabled: true - -Style/RedundantReturn: - Description: "Don't use return where it's not required." - Enabled: true - -Style/RedundantSelf: - Description: "Don't use self where it's not needed." - Enabled: true - -Style/RegexpLiteral: - Description: >- - Use %r for regular expressions matching more than - `MaxSlashes` '/' characters. - Use %r only for regular expressions matching more than - `MaxSlashes` '/' character. - Enabled: false - -Style/RescueModifier: - Description: 'Avoid using rescue in its modifier form.' - Enabled: true - -Style/SelfAssignment: - Description: >- - Checks for places where self-assignment shorthand should have - been used. - Enabled: true - -Style/Semicolon: - Description: "Don't use semicolons to terminate expressions." - Enabled: true - -Style/SignalException: - Description: 'Checks for proper usage of fail and raise.' - Enabled: false - -Style/SingleLineBlockParams: - Description: 'Enforces the names of some block params.' - Enabled: true - -Style/SingleLineMethods: - Description: 'Avoid single-line methods.' - Enabled: true - -Style/SpaceBeforeFirstArg: - Description: >- - Checks that exactly one space is used between a method name - and the first argument for method calls without parentheses. - Enabled: true - -Style/SpaceAfterColon: - Description: 'Use spaces after colons.' - Enabled: true - -Style/SpaceAfterComma: - Description: 'Use spaces after commas.' - Enabled: true - -Style/SpaceAfterControlKeyword: - Description: 'Use spaces after if/elsif/unless/while/until/case/when.' - Enabled: true - -Style/SpaceAfterMethodName: - Description: >- - Never put a space between a method name and the opening - parenthesis in a method definition. - Enabled: true - -Style/SpaceAfterNot: - Description: Tracks redundant space after the ! operator. - Enabled: true - -Style/SpaceAfterSemicolon: - Description: 'Use spaces after semicolons.' - Enabled: true - -Style/SpaceBeforeBlockBraces: - Description: >- - Checks that the left block brace has or doesn't have space - before it. - Enabled: true - -Style/SpaceBeforeComma: - Description: 'No spaces before commas.' - Enabled: true - -Style/SpaceBeforeComment: - Description: >- - Checks for missing space between code and a comment on the - same line. - Enabled: true - -Style/SpaceBeforeSemicolon: - Description: 'No spaces before semicolons.' - Enabled: true - -Style/SpaceInsideBlockBraces: - Description: >- - Checks that block braces have or don't have surrounding space. - For blocks taking parameters, checks that the left brace has - or doesn't have trailing space. - Enabled: true - -Style/SpaceAroundEqualsInParameterDefault: - Description: >- - Checks that the equals signs in parameter default assignments - have or don't have surrounding space depending on - configuration. - Enabled: true - -Style/SpaceAroundOperators: - Description: 'Use spaces around operators.' - Enabled: true - -Style/SpaceBeforeModifierKeyword: - Description: 'Put a space before the modifier keyword.' - Enabled: true - -Style/SpaceInsideBrackets: - Description: 'No spaces after [ or before ].' - Enabled: true - -Style/SpaceInsideHashLiteralBraces: - Description: "Use spaces inside hash literal braces - or don't." - Enabled: true - EnforcedStyle: no_space - -Style/SpaceInsideParens: - Description: 'No spaces after ( or before ).' - Enabled: true - -Style/SpecialGlobalVars: - Description: 'Avoid Perl-style global variables.' - Enabled: true - -Style/StringLiterals: - Description: 'Checks if uses of quotes match the configured preference.' - Enabled: true - EnforcedStyle: single_quotes - -Style/Tab: - Description: 'No hard tabs.' - Enabled: true - -Style/TrailingBlankLines: - Description: 'Checks trailing blank lines and final newline.' - Enabled: true - -Style/TrailingCommaInArguments: - EnforcedStyleForMultiline: comma - Description: 'Checks for trailing comma in argument lists.' - Enabled: true - -Style/TrailingCommaInLiteral: - EnforcedStyleForMultiline: comma - Description: 'Checks for trailing comma in array and hash literals.' - Enabled: true - -Style/TrailingWhitespace: - Description: 'Avoid trailing whitespace.' - Enabled: true - -Style/TrivialAccessors: - Description: 'Prefer attr_* methods to trivial readers/writers.' - Enabled: true - -Style/UnlessElse: - Description: >- - Never use unless with else. Rewrite these with the positive - case first. - Enabled: true - -Style/UnneededCapitalW: - Description: 'Checks for %W when interpolation is not needed.' - Enabled: true - -Style/UnneededPercentQ: - Description: 'Checks for %q/%Q when single quotes or double quotes would do.' - Enabled: true - -Style/VariableInterpolation: - Description: >- - Don't interpolate global, instance and class variables - directly in strings. - Enabled: true - -Style/VariableName: - Description: 'Use the configured style when naming variables.' - Enabled: true - -Style/WhenThen: - Description: 'Use when x then ... for one-line cases.' - Enabled: true - -Style/WhileUntilDo: - Description: 'Checks for redundant do after while or until.' - Enabled: true - -Style/WhileUntilModifier: - Description: >- - Favor modifier while/until usage when you have a - single-line body. - Enabled: true - -Style/WordArray: - Description: 'Use %w or %W for arrays of words.' - Enabled: true - -#################### Metrics ################################ - -Metrics/BlockNesting: - Description: 'Avoid excessive block nesting' - Enabled: true - -Metrics/ClassLength: - Description: 'Avoid classes longer than 100 lines of code.' - Enabled: true - -Metrics/CyclomaticComplexity: - Description: >- - A complexity metric that is strongy correlated to the number - of test cases needed to validate a method. - Enabled: true - -Metrics/LineLength: - Description: 'Limit lines to 80 characters.' - Enabled: true - -Metrics/MethodLength: - Description: 'Avoid methods longer than 10 lines of code.' - Enabled: true - -Metrics/ParameterLists: - Description: 'Avoid parameter lists longer than three or four parameters.' - Enabled: true - -Metrics/PerceivedComplexity: - Description: >- - A complexity metric geared towards measuring complexity for a - human reader. - Enabled: true - -#################### Lint ################################ -### Warnings - -Lint/AmbiguousOperator: - Description: >- - Checks for ambiguous operators in the first argument of a - method invocation without parentheses. - Enabled: true - -Lint/AmbiguousRegexpLiteral: - Description: >- - Checks for ambiguous regexp literals in the first argument of - a method invocation without parenthesis. - Enabled: true - -Lint/AssignmentInCondition: - Description: "Don't use assignment in conditions." - Enabled: false - -Lint/BlockAlignment: - Description: 'Align block ends correctly.' - Enabled: true - -Lint/ConditionPosition: - Description: >- - Checks for condition placed in a confusing position relative to - the keyword. - Enabled: true - -Lint/Debugger: - Description: 'Check for debugger calls.' - Enabled: true - -Lint/DefEndAlignment: - Description: 'Align ends corresponding to defs correctly.' - Enabled: true - -Lint/DeprecatedClassMethods: - Description: 'Check for deprecated class method calls.' - Enabled: true - -Lint/ElseLayout: - Description: 'Check for odd code arrangement in an else block.' - Enabled: true - -Lint/EmptyEnsure: - Description: 'Checks for empty ensure block.' - Enabled: true - -Lint/EmptyInterpolation: - Description: 'Checks for empty string interpolation.' - Enabled: true - -Lint/EndAlignment: - Description: 'Align ends correctly.' - Enabled: true - -Lint/EndInMethod: - Description: 'END blocks should not be placed inside method definitions.' - Enabled: true - -Lint/EnsureReturn: - Description: 'Never use return in an ensure block.' - Enabled: true - -Lint/Eval: - Description: 'The use of eval represents a serious security risk.' - Enabled: true - -Lint/HandleExceptions: - Description: "Don't suppress exception." - Enabled: true - -Lint/InvalidCharacterLiteral: - Description: >- - Checks for invalid character literals with a non-escaped - whitespace character. - Enabled: true - -Lint/LiteralInCondition: - Description: 'Checks of literals used in conditions.' - Enabled: true - -Lint/LiteralInInterpolation: - Description: 'Checks for literals used in interpolation.' - Enabled: true - -Lint/Loop: - Description: >- - Use Kernel#loop with break rather than begin/end/until or - begin/end/while for post-loop tests. - Enabled: true - -Lint/ParenthesesAsGroupedExpression: - Description: >- - Checks for method calls with a space before the opening - parenthesis. - Enabled: true - -Lint/RequireParentheses: - Description: >- - Use parentheses in the method call to avoid confusion - about precedence. - Enabled: true - -Lint/RescueException: - Description: 'Avoid rescuing the Exception class.' - Enabled: true - -Lint/ShadowingOuterLocalVariable: - Description: >- - Do not use the same name as outer local variable - for block arguments or block local variables. - Enabled: true - -Lint/StringConversionInInterpolation: - Description: 'Checks for Object#to_s usage in string interpolation.' - Enabled: true - -Lint/UnderscorePrefixedVariableName: - Description: 'Do not use prefix `_` for a variable that is used.' - Enabled: true - -Lint/UnusedBlockArgument: - Description: 'Checks for unused block arguments.' - Enabled: true - -Lint/UnusedMethodArgument: - Description: 'Checks for unused method arguments.' - Enabled: true - -Lint/UnreachableCode: - Description: 'Unreachable code.' - Enabled: true - -Lint/UselessAccessModifier: - Description: 'Checks for useless access modifiers.' - Enabled: true - -Lint/UselessAssignment: - Description: 'Checks for useless assignment to a local variable.' - Enabled: true - -Lint/UselessComparison: - Description: 'Checks for comparison of something with itself.' - Enabled: true - -Lint/UselessElseWithoutRescue: - Description: 'Checks for useless `else` in `begin..end` without `rescue`.' - Enabled: true - -Lint/UselessSetterCall: - Description: 'Checks for useless setter call to a local variable.' - Enabled: true - -Lint/Void: - Description: 'Possible use of operator/literal/variable in void context.' - Enabled: true - -##################### Rails ################################## - -Rails/ActionFilter: - Description: 'Enforces consistent use of action filter methods.' - Enabled: false - -Rails/Delegate: - Description: 'Prefer delegate method for delegations.' - Enabled: true - -Rails/HasAndBelongsToMany: - Description: 'Prefer has_many :through to has_and_belongs_to_many.' - Enabled: false - -Rails/Output: - Description: 'Checks for calls to puts, print, etc.' - Enabled: true - -Rails/ReadWriteAttribute: - Description: >- - Checks for read_attribute(:attr) and - write_attribute(:attr, val). - Enabled: true - -Rails/ScopeArgs: - Description: 'Checks the arguments of ActiveRecord scopes.' - Enabled: true - -Rails/Validation: - Description: 'Use sexy validations.' - Enabled: true +inherit_from: .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..d35e368f6 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,847 @@ +AllCops: + Exclude: + - 'test/**/*' + - 'spec/**/*' + - 'features/**/*' + - 'script/**/*' + +# These are all the cops that are enabled in the default configuration. + +Style/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + Enabled: true + +Style/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: true + +Style/Alias: + Description: 'Use alias_method instead of alias.' + Enabled: true + +Style/AlignArray: + Description: >- + Align the elements of an array literal if they span more than + one line. + Enabled: true + +Style/AlignHash: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: true + +Style/AlignParameters: + Description: >- + Align the parameters of a method call if they span more + than one line. + Enabled: false + +Style/AndOr: + Description: 'Use &&/|| instead of and/or.' + Enabled: true + +Style/ArrayJoin: + Description: 'Use Array#join instead of Array#*.' + Enabled: true + +Style/AsciiComments: + Description: 'Use only ascii symbols in comments.' + Enabled: true + +Style/AsciiIdentifiers: + Description: 'Use only ascii symbols in identifiers.' + Enabled: true + +Style/Attr: + Description: 'Checks for uses of Module#attr.' + Enabled: true + +Style/BeginBlock: + Description: 'Avoid the use of BEGIN blocks.' + Enabled: true + +Style/BarePercentLiterals: + Description: 'Checks if usage of %() or %Q() matches configuration.' + Enabled: true + +Style/BlockComments: + Description: 'Do not use block comments.' + Enabled: true + +Style/BlockEndNewline: + Description: 'Put end statement of multiline block on its own line.' + Enabled: true + +Style/BlockDelimiters: + Description: >- + Avoid using {...} for multi-line blocks (multiline chaining is + always ugly). + Prefer {...} over do...end for single-line blocks. + Enabled: true + +Style/BracesAroundHashParameters: + Description: 'Enforce braces style inside hash parameters.' + Enabled: true + +Style/CaseEquality: + Description: 'Avoid explicit use of the case equality operator(===).' + Enabled: true + +Style/CaseIndentation: + Description: 'Indentation of when in a case/when/[else/]end.' + Enabled: true + +Style/CharacterLiteral: + Description: 'Checks for uses of character literals.' + Enabled: true + +Style/ClassAndModuleCamelCase: + Description: 'Use CamelCase for classes and modules.' + Enabled: true + +Style/ClassAndModuleChildren: + Description: 'Checks style of children classes and modules.' + Enabled: false + +Style/ClassCheck: + Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' + Enabled: true + +Style/ClassMethods: + Description: 'Use self when defining module/class methods.' + Enabled: true + +Style/ClassVars: + Description: 'Avoid the use of class variables.' + Enabled: true + +Style/CollectionMethods: + Description: 'Preferred collection methods.' + Enabled: true + PreferredMethods: + collect: 'map' + collect!: 'map!' + inject: 'reduce' + detect: 'find' + find_all: 'select' + + +Style/ColonMethodCall: + Description: 'Do not use :: for method call.' + Enabled: true + +Style/CommentAnnotation: + Description: >- + Checks formatting of special comments + (TODO, FIXME, OPTIMIZE, HACK, REVIEW). + Enabled: true + +Style/CommentIndentation: + Description: 'Indentation of comments.' + Enabled: true + +Style/ConstantName: + Description: 'Constants should use SCREAMING_SNAKE_CASE.' + Enabled: true + +Style/DefWithParentheses: + Description: 'Use def with parentheses when there are arguments.' + Enabled: true + +Style/DeprecatedHashMethods: + Description: 'Checks for use of deprecated Hash methods.' + Enabled: true + +Style/Documentation: + Description: 'Document classes and non-namespace modules.' + Enabled: false + +Style/DotPosition: + Description: 'Checks the position of the dot in multi-line method calls.' + Enabled: true + +Style/DoubleNegation: + Description: 'Checks for uses of double negation (!!).' + Enabled: false + +Style/EachWithObject: + Description: 'Prefer `each_with_object` over `inject` or `reduce`.' + Enabled: false + +Style/EmptyLineBetweenDefs: + Description: 'Use empty lines between defs.' + Enabled: true + +Style/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: true + +Style/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + Enabled: false + +Style/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + Enabled: true + +Style/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around class bodies." + Enabled: true + +Style/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around module bodies." + Enabled: true + +Style/EmptyLinesAroundMethodBody: + Description: "Keeps track of empty lines around method bodies." + Enabled: true + +Style/EmptyLiteral: + Description: 'Prefer literals to Array.new/Hash.new/String.new.' + Enabled: true + +Style/Encoding: + Description: 'Use UTF-8 as the source file encoding.' + Enabled: true + +Style/EndBlock: + Description: 'Avoid the use of END blocks.' + Enabled: true + +Style/EndOfLine: + Description: 'Use Unix-style line endings.' + Enabled: true + +Style/EvenOdd: + Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' + Enabled: true + +Style/FileName: + Description: 'Use snake_case for source file names.' + Enabled: false + Exclude: + - 'db/**/*' + +Style/FlipFlop: + Description: 'Checks for flip flops' + Enabled: true + +Style/For: + Description: 'Checks use of for or each in multiline loops.' + Enabled: true + +Style/FormatString: + Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' + Enabled: true + +Style/GlobalVars: + Description: 'Do not introduce global variables.' + Enabled: true + +Style/GuardClause: + Description: 'Check for conditionals that can be replaced with guard clauses' + Enabled: false + +Style/HashSyntax: + Description: >- + Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax + { :a => 1, :b => 2 }. + Enabled: true + +Style/IfUnlessModifier: + Description: >- + Favor modifier if/unless usage when you have a + single-line body. + Enabled: true + +Style/IfWithSemicolon: + Description: 'Never use if x; .... Use the ternary operator instead.' + Enabled: true + +Style/IndentationConsistency: + Description: 'Keep indentation straight.' + Enabled: true + +Style/IndentationWidth: + Description: 'Use 2 spaces for indentation.' + Enabled: true + +Style/IndentArray: + Description: >- + Checks the indentation of the first element in an array + literal. + Enabled: true + +Style/IndentHash: + Description: 'Checks the indentation of the first key in a hash literal.' + Enabled: false + +Style/Lambda: + Description: 'Use the new lambda literal syntax for single-line blocks.' + Enabled: false + +Style/LambdaCall: + Description: 'Use lambda.call(...) instead of lambda.(...).' + Enabled: true + +Style/LeadingCommentSpace: + Description: 'Comments should start with a space.' + Enabled: true + +Style/LineEndConcatenation: + Description: >- + Use \ instead of + or << to concatenate two string literals at + line end. + Enabled: true + +Style/MethodCallParentheses: + Description: 'Do not use parentheses for method calls with no arguments.' + Enabled: true + +Style/MethodDefParentheses: + Description: >- + Checks if the method definitions have or don't have + parentheses. + Enabled: true + +Style/MethodName: + Description: 'Use the configured style when naming methods.' + Enabled: true + +Style/ModuleFunction: + Description: 'Checks for usage of `extend self` in modules.' + Enabled: true + +Style/MultilineBlockChain: + Description: 'Avoid multi-line chains of blocks.' + Enabled: true + +Style/MultilineBlockLayout: + Description: 'Ensures newlines after multiline block do statements.' + Enabled: true + +Style/MultilineIfThen: + Description: 'Never use then for multi-line if/unless.' + Enabled: true + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + Enabled: true + +Style/NegatedIf: + Description: >- + Favor unless over if for negative conditions + (or control flow or). + Enabled: true + +Style/NegatedWhile: + Description: 'Favor until over while for negative conditions.' + Enabled: true + +Style/NestedTernaryOperator: + Description: 'Use one expression per branch in a ternary operator.' + Enabled: true + +Style/Next: + Description: 'Use `next` to skip iteration instead of a condition at the end.' + Enabled: true + +Style/NilComparison: + Description: 'Prefer x.nil? to x == nil.' + Enabled: true + +Style/NonNilCheck: + Description: 'Checks for redundant nil checks.' + Enabled: true + +Style/Not: + Description: 'Use ! instead of not.' + Enabled: true + +Style/NumericLiterals: + Description: >- + Add underscores to large numeric literals to improve their + readability. + Enabled: true + +Style/OneLineConditional: + Description: >- + Favor the ternary operator(?:) over + if/then/else/end constructs. + Enabled: true + +Style/OpMethod: + Description: 'When defining binary operators, name the argument other.' + Enabled: true + +Style/ParenthesesAroundCondition: + Description: >- + Don't use parentheses around the condition of an + if/unless/while. + Enabled: true + +Style/PercentLiteralDelimiters: + Description: 'Use `%`-literal delimiters consistently' + Enabled: true + +Style/PercentQLiterals: + Description: 'Checks if uses of %Q/%q match the configured preference.' + Enabled: true + +Style/PerlBackrefs: + Description: 'Avoid Perl-style regex back references.' + Enabled: true + +Style/PredicateName: + Description: 'Check the names of predicate methods.' + Enabled: true + +Style/Proc: + Description: 'Use proc instead of Proc.new.' + Enabled: true + +Style/RaiseArgs: + Description: 'Checks the arguments passed to raise/fail.' + Enabled: false + +Style/RedundantBegin: + Description: "Don't use begin blocks when they are not needed." + Enabled: true + +Style/RedundantException: + Description: "Checks for an obsolete RuntimeException argument in raise/fail." + Enabled: true + +Style/RedundantReturn: + Description: "Don't use return where it's not required." + Enabled: true + +Style/RedundantSelf: + Description: "Don't use self where it's not needed." + Enabled: true + +Style/RegexpLiteral: + Description: >- + Use %r for regular expressions matching more than + `MaxSlashes` '/' characters. + Use %r only for regular expressions matching more than + `MaxSlashes` '/' character. + Enabled: false + +Style/RescueModifier: + Description: 'Avoid using rescue in its modifier form.' + Enabled: true + +Style/SelfAssignment: + Description: >- + Checks for places where self-assignment shorthand should have + been used. + Enabled: true + +Style/Semicolon: + Description: "Don't use semicolons to terminate expressions." + Enabled: true + +Style/SignalException: + Description: 'Checks for proper usage of fail and raise.' + Enabled: false + +Style/SingleLineBlockParams: + Description: 'Enforces the names of some block params.' + Enabled: true + +Style/SingleLineMethods: + Description: 'Avoid single-line methods.' + Enabled: true + +Style/SpaceBeforeFirstArg: + Description: >- + Checks that exactly one space is used between a method name + and the first argument for method calls without parentheses. + Enabled: true + +Style/SpaceAfterColon: + Description: 'Use spaces after colons.' + Enabled: true + +Style/SpaceAfterComma: + Description: 'Use spaces after commas.' + Enabled: true + +Style/SpaceAfterControlKeyword: + Description: 'Use spaces after if/elsif/unless/while/until/case/when.' + Enabled: true + +Style/SpaceAfterMethodName: + Description: >- + Never put a space between a method name and the opening + parenthesis in a method definition. + Enabled: true + +Style/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + Enabled: true + +Style/SpaceAfterSemicolon: + Description: 'Use spaces after semicolons.' + Enabled: true + +Style/SpaceBeforeBlockBraces: + Description: >- + Checks that the left block brace has or doesn't have space + before it. + Enabled: true + +Style/SpaceBeforeComma: + Description: 'No spaces before commas.' + Enabled: true + +Style/SpaceBeforeComment: + Description: >- + Checks for missing space between code and a comment on the + same line. + Enabled: true + +Style/SpaceBeforeSemicolon: + Description: 'No spaces before semicolons.' + Enabled: true + +Style/SpaceInsideBlockBraces: + Description: >- + Checks that block braces have or don't have surrounding space. + For blocks taking parameters, checks that the left brace has + or doesn't have trailing space. + Enabled: true + +Style/SpaceAroundEqualsInParameterDefault: + Description: >- + Checks that the equals signs in parameter default assignments + have or don't have surrounding space depending on + configuration. + Enabled: true + +Style/SpaceAroundOperators: + Description: 'Use spaces around operators.' + Enabled: true + +Style/SpaceBeforeModifierKeyword: + Description: 'Put a space before the modifier keyword.' + Enabled: true + +Style/SpaceInsideBrackets: + Description: 'No spaces after [ or before ].' + Enabled: true + +Style/SpaceInsideHashLiteralBraces: + Description: "Use spaces inside hash literal braces - or don't." + Enabled: true + EnforcedStyle: no_space + +Style/SpaceInsideParens: + Description: 'No spaces after ( or before ).' + Enabled: true + +Style/SpecialGlobalVars: + Description: 'Avoid Perl-style global variables.' + Enabled: true + +Style/StringLiterals: + Description: 'Checks if uses of quotes match the configured preference.' + Enabled: true + EnforcedStyle: single_quotes + +Style/Tab: + Description: 'No hard tabs.' + Enabled: true + +Style/TrailingBlankLines: + Description: 'Checks trailing blank lines and final newline.' + Enabled: true + +Style/TrailingCommaInArguments: + EnforcedStyleForMultiline: comma + Description: 'Checks for trailing comma in argument lists.' + Enabled: true + +Style/TrailingCommaInLiteral: + EnforcedStyleForMultiline: comma + Description: 'Checks for trailing comma in array and hash literals.' + Enabled: true + +Style/TrailingWhitespace: + Description: 'Avoid trailing whitespace.' + Enabled: true + +Style/TrivialAccessors: + Description: 'Prefer attr_* methods to trivial readers/writers.' + Enabled: true + +Style/UnlessElse: + Description: >- + Never use unless with else. Rewrite these with the positive + case first. + Enabled: true + +Style/UnneededCapitalW: + Description: 'Checks for %W when interpolation is not needed.' + Enabled: true + +Style/UnneededPercentQ: + Description: 'Checks for %q/%Q when single quotes or double quotes would do.' + Enabled: true + +Style/VariableInterpolation: + Description: >- + Don't interpolate global, instance and class variables + directly in strings. + Enabled: true + +Style/VariableName: + Description: 'Use the configured style when naming variables.' + Enabled: true + +Style/WhenThen: + Description: 'Use when x then ... for one-line cases.' + Enabled: true + +Style/WhileUntilDo: + Description: 'Checks for redundant do after while or until.' + Enabled: true + +Style/WhileUntilModifier: + Description: >- + Favor modifier while/until usage when you have a + single-line body. + Enabled: true + +Style/WordArray: + Description: 'Use %w or %W for arrays of words.' + Enabled: true + +#################### Metrics ################################ + +Metrics/BlockNesting: + Description: 'Avoid excessive block nesting' + Enabled: true + +Metrics/ClassLength: + Description: 'Avoid classes longer than 100 lines of code.' + Enabled: true + +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongy correlated to the number + of test cases needed to validate a method. + Enabled: true + +Metrics/LineLength: + Description: 'Limit lines to 80 characters.' + Enabled: true + +Metrics/MethodLength: + Description: 'Avoid methods longer than 10 lines of code.' + Enabled: true + +Metrics/ParameterLists: + Description: 'Avoid parameter lists longer than three or four parameters.' + Enabled: true + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: true + +#################### Lint ################################ +### Warnings + +Lint/AmbiguousOperator: + Description: >- + Checks for ambiguous operators in the first argument of a + method invocation without parentheses. + Enabled: true + +Lint/AmbiguousRegexpLiteral: + Description: >- + Checks for ambiguous regexp literals in the first argument of + a method invocation without parenthesis. + Enabled: true + +Lint/AssignmentInCondition: + Description: "Don't use assignment in conditions." + Enabled: false + +Lint/BlockAlignment: + Description: 'Align block ends correctly.' + Enabled: true + +Lint/ConditionPosition: + Description: >- + Checks for condition placed in a confusing position relative to + the keyword. + Enabled: true + +Lint/Debugger: + Description: 'Check for debugger calls.' + Enabled: true + +Lint/DefEndAlignment: + Description: 'Align ends corresponding to defs correctly.' + Enabled: true + +Lint/DeprecatedClassMethods: + Description: 'Check for deprecated class method calls.' + Enabled: true + +Lint/ElseLayout: + Description: 'Check for odd code arrangement in an else block.' + Enabled: true + +Lint/EmptyEnsure: + Description: 'Checks for empty ensure block.' + Enabled: true + +Lint/EmptyInterpolation: + Description: 'Checks for empty string interpolation.' + Enabled: true + +Lint/EndAlignment: + Description: 'Align ends correctly.' + Enabled: true + +Lint/EndInMethod: + Description: 'END blocks should not be placed inside method definitions.' + Enabled: true + +Lint/EnsureReturn: + Description: 'Never use return in an ensure block.' + Enabled: true + +Lint/Eval: + Description: 'The use of eval represents a serious security risk.' + Enabled: true + +Lint/HandleExceptions: + Description: "Don't suppress exception." + Enabled: true + +Lint/InvalidCharacterLiteral: + Description: >- + Checks for invalid character literals with a non-escaped + whitespace character. + Enabled: true + +Lint/LiteralInCondition: + Description: 'Checks of literals used in conditions.' + Enabled: true + +Lint/LiteralInInterpolation: + Description: 'Checks for literals used in interpolation.' + Enabled: true + +Lint/Loop: + Description: >- + Use Kernel#loop with break rather than begin/end/until or + begin/end/while for post-loop tests. + Enabled: true + +Lint/ParenthesesAsGroupedExpression: + Description: >- + Checks for method calls with a space before the opening + parenthesis. + Enabled: true + +Lint/RequireParentheses: + Description: >- + Use parentheses in the method call to avoid confusion + about precedence. + Enabled: true + +Lint/RescueException: + Description: 'Avoid rescuing the Exception class.' + Enabled: true + +Lint/ShadowingOuterLocalVariable: + Description: >- + Do not use the same name as outer local variable + for block arguments or block local variables. + Enabled: true + +Lint/StringConversionInInterpolation: + Description: 'Checks for Object#to_s usage in string interpolation.' + Enabled: true + +Lint/UnderscorePrefixedVariableName: + Description: 'Do not use prefix `_` for a variable that is used.' + Enabled: true + +Lint/UnusedBlockArgument: + Description: 'Checks for unused block arguments.' + Enabled: true + +Lint/UnusedMethodArgument: + Description: 'Checks for unused method arguments.' + Enabled: true + +Lint/UnreachableCode: + Description: 'Unreachable code.' + Enabled: true + +Lint/UselessAccessModifier: + Description: 'Checks for useless access modifiers.' + Enabled: true + +Lint/UselessAssignment: + Description: 'Checks for useless assignment to a local variable.' + Enabled: true + +Lint/UselessComparison: + Description: 'Checks for comparison of something with itself.' + Enabled: true + +Lint/UselessElseWithoutRescue: + Description: 'Checks for useless `else` in `begin..end` without `rescue`.' + Enabled: true + +Lint/UselessSetterCall: + Description: 'Checks for useless setter call to a local variable.' + Enabled: true + +Lint/Void: + Description: 'Possible use of operator/literal/variable in void context.' + Enabled: true + +##################### Rails ################################## + +Rails/ActionFilter: + Description: 'Enforces consistent use of action filter methods.' + Enabled: false + +Rails/Delegate: + Description: 'Prefer delegate method for delegations.' + Enabled: true + +Rails/HasAndBelongsToMany: + Description: 'Prefer has_many :through to has_and_belongs_to_many.' + Enabled: false + +Rails/Output: + Description: 'Checks for calls to puts, print, etc.' + Enabled: true + +Rails/ReadWriteAttribute: + Description: >- + Checks for read_attribute(:attr) and + write_attribute(:attr, val). + Enabled: true + +Rails/ScopeArgs: + Description: 'Checks the arguments of ActiveRecord scopes.' + Enabled: true + +Rails/Validation: + Description: 'Use sexy validations.' + Enabled: true diff --git a/script/stylecheck b/script/stylecheck index 984c1bbf4..36e644892 100755 --- a/script/stylecheck +++ b/script/stylecheck @@ -1,19 +1,3 @@ -#!/usr/bin/env ruby -require 'tempfile' +#!/bin/bash -RAILS_ROOT = File.expand_path('../..', __FILE__) -CONFIG_FILE_PATH = File.join(RAILS_ROOT, '.hound.yml') - -config = File.read(CONFIG_FILE_PATH) -config = config.sub(/$\s+ShowCopNames:.*?\n/, '') - -customized_config_file = Tempfile.new('rubocop.yml') -customized_config_file.write(config) -customized_config_file.close - -command = %W(rubocop --rails --config #{customized_config_file.path}) -command += ARGV - -system(*command) - -customized_config_file.unlink +rubocop --rails $@ From 5a603707070360a93947a6a0743a104c048dfe8c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 16 Jan 2016 18:35:36 +0100 Subject: [PATCH 156/240] Allow to omit the utf8 comment. --- .rubocop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index d35e368f6..ef627ce24 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,7 @@ AllCops: - 'spec/**/*' - 'features/**/*' - 'script/**/*' + TargetRubyVersion: 2.1 # These are all the cops that are enabled in the default configuration. @@ -203,6 +204,7 @@ Style/EmptyLiteral: Style/Encoding: Description: 'Use UTF-8 as the source file encoding.' + EnforcedStyle: when_needed Enabled: true Style/EndBlock: From 4619a1c60c7472dfd97770ae4938a79a9ddd5134 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:10:16 +0100 Subject: [PATCH 157/240] Add default haml-lint condig. --- .haml-lint.yml | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .haml-lint.yml diff --git a/.haml-lint.yml b/.haml-lint.yml new file mode 100644 index 000000000..5b3687960 --- /dev/null +++ b/.haml-lint.yml @@ -0,0 +1,70 @@ +# Whether to ignore frontmatter at the beginning of HAML documents for +# frameworks such as Jekyll/Middleman +skip_frontmatter: false + +linters: + AltText: + enabled: false + + ClassAttributeWithStaticValue: + enabled: true + + ClassesBeforeIds: + enabled: true + + ConsecutiveComments: + enabled: true + + ConsecutiveSilentScripts: + enabled: true + max_consecutive: 2 + + EmptyScript: + enabled: true + + HtmlAttributes: + enabled: true + + ImplicitDiv: + enabled: true + + LeadingCommentSpace: + enabled: true + + LineLength: + enabled: true + max: 80 + + MultilinePipe: + enabled: true + + MultilineScript: + enabled: true + + ObjectReferenceAttributes: + enabled: true + + RuboCop: + enabled: false + + RubyComments: + enabled: true + + SpaceBeforeScript: + enabled: true + + SpaceInsideHashAttributes: + enabled: true + style: space + + TagName: + enabled: true + + TrailingWhitespace: + enabled: true + + UnnecessaryInterpolation: + enabled: true + + UnnecessaryStringOutput: + enabled: true From 72a7cc66d2bf5836f4766843cd160049c5a75f68 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:10:32 +0100 Subject: [PATCH 158/240] Remove haml line length check. --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index 5b3687960..29b0c2780 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -32,7 +32,7 @@ linters: enabled: true LineLength: - enabled: true + enabled: false max: 80 MultilinePipe: From 8f23bf758e0b2b31c5e8b76c25beaa26018d35d0 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:20:49 +0100 Subject: [PATCH 159/240] Add default coffeelint style file. --- .coffeescript-style.json | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .coffeescript-style.json diff --git a/.coffeescript-style.json b/.coffeescript-style.json new file mode 100644 index 000000000..c76b419e5 --- /dev/null +++ b/.coffeescript-style.json @@ -0,0 +1,98 @@ +{ + "arrow_spacing": { + "level": "error" + }, + "camel_case_classes": { + "level": "error" + }, + "coffeescript_error": { + "level": "error" + }, + "colon_assignment_spacing": { + "level": "error", + "spacing": { + "left": 0, + "right": 1 + } + }, + "cyclomatic_complexity": { + "level": "ignore", + "value": 10 + }, + "duplicate_key": { + "level": "error" + }, + "empty_constructor_needs_parens": { + "level": "ignore" + }, + "indentation": { + "level": "error", + "value": 2 + }, + "line_endings": { + "level": "ignore", + "value": "unix" + }, + "max_line_length": { + "level": "error", + "value": 80 + }, + "missing_fat_arrows": { + "level": "ignore" + }, + "newlines_after_classes": { + "level": "error", + "value": 1 + }, + "no_backticks": { + "level": "ignore" + }, + "no_debugger": { + "level": "error" + }, + "no_empty_functions": { + "level": "error" + }, + "no_empty_param_list": { + "level": "error" + }, + "no_implicit_braces": { + "level": "ignore" + }, + "no_implicit_parens": { + "level": "ignore" + }, + "no_interpolation_in_single_quotes": { + "level": "error" + }, + "no_plusplus": { + "level": "ignore" + }, + "no_stand_alone_at": { + "level": "error" + }, + "no_tabs": { + "level": "error" + }, + "no_throwing_strings": { + "level": "error" + }, + "no_trailing_semicolons": { + "level": "error" + }, + "no_trailing_whitespace": { + "level": "error" + }, + "no_unnecessary_double_quotes": { + "level": "ignore" + }, + "no_unnecessary_fat_arrows": { + "level": "error" + }, + "non_empty_constructor_needs_parens": { + "level": "ignore" + }, + "space_operators": { + "level": "ignore" + } +} From ba7107aaf0546850216cb5c92637b5fdb27187fe Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:21:14 +0100 Subject: [PATCH 160/240] Add default JSHint style file. --- .javascript-style.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .javascript-style.json diff --git a/.javascript-style.json b/.javascript-style.json new file mode 100644 index 000000000..27d04f122 --- /dev/null +++ b/.javascript-style.json @@ -0,0 +1,33 @@ +{ + "asi": false, + "bitwise": true, + "browser": true, + "camelcase": true, + "curly": true, + "forin": true, + "immed": true, + "latedef": "nofunc", + "maxlen": 80, + "newcap": true, + "noarg": true, + "noempty": true, + "nonew": true, + "predef": [ + "$", + "jQuery", + + "jasmine", + "beforeEach", + "describe", + "expect", + "it", + + "angular", + "inject", + "module" + ], + "quotmark": true, + "trailing": true, + "undef": true, + "unused": true +} From b7ef4ac3c06f62cc6cfc6d9ef01bc388ed92b369 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:21:41 +0100 Subject: [PATCH 161/240] Use config as suggested by the hound docs. --- .hound.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.hound.yml b/.hound.yml index 86a41a504..60a7a6043 100644 --- a/.hound.yml +++ b/.hound.yml @@ -1,3 +1,17 @@ ShowCopNames: true -inherit_from: .rubocop.yml +ruby: + enabled: true + config_file: .rubocop.yml + +haml: + enabled: true + config_file: .haml-lint.yml + +coffeescript: + enabled: true + config_file: .coffeescript-style.json + +javascript: + enabled: true + config_file: .javascript-style.json From df58acd0ffc385926fd5ee9f1774a0fc08744b5e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:47:11 +0100 Subject: [PATCH 162/240] Fix dot position. --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index ef627ce24..f0c278405 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -160,6 +160,7 @@ Style/Documentation: Style/DotPosition: Description: 'Checks the position of the dot in multi-line method calls.' + EnforcedStyle: trailing Enabled: true Style/DoubleNegation: From b101e70265b4c1824c86fff84e0fd36eab8c60c4 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:49:41 +0100 Subject: [PATCH 163/240] Display cop names ins stylecheck script. --- script/stylecheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/stylecheck b/script/stylecheck index 36e644892..1ed90856f 100755 --- a/script/stylecheck +++ b/script/stylecheck @@ -1,3 +1,3 @@ #!/bin/bash -rubocop --rails $@ +rubocop --display-cop-names --rails $@ From 6b66fb2ad38fa389c4941a7dd2d3f0e6475b8f01 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:51:40 +0100 Subject: [PATCH 164/240] Fix alignment of method calls. --- .rubocop.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index f0c278405..f043b2d95 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -334,6 +334,10 @@ Style/MultilineTernaryOperator: use if/unless instead. Enabled: true +Style/MultilineMethodCallIndentation: + EnforcedStyle: indented + Enabled: true + Style/NegatedIf: Description: >- Favor unless over if for negative conditions From ba528a851a426822b7e7bb52b7557abbf6add781 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 18:08:35 +0100 Subject: [PATCH 165/240] Specify access validations more clearly. --- app/models/repository/access.rb | 3 ++- app/models/repository/validations.rb | 17 ++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/models/repository/access.rb b/app/models/repository/access.rb index 53617ea88..c53c010c5 100644 --- a/app/models/repository/access.rb +++ b/app/models/repository/access.rb @@ -1,7 +1,8 @@ module Repository::Access extend ActiveSupport::Concern - OPTIONS = %w[public_r public_rw private_r private_rw] + OPTIONS = %w[public_r public_rw private_rw] + OPTIONS_MIRROR = %w[public_r private_r] DEFAULT_OPTION = OPTIONS[0] def self.as_read_only(access) diff --git a/app/models/repository/validations.rb b/app/models/repository/validations.rb index 002571ea7..a48744437 100644 --- a/app/models/repository/validations.rb +++ b/app/models/repository/validations.rb @@ -24,19 +24,14 @@ module Repository::Validations validates :remote_type, presence: true, if: :source_address? validates_with RemoteTypeValidator - validates_with RemoteAccessValidator validates :access, presence: true, - inclusion: {in: Repository::Access::OPTIONS} - end - - class RemoteAccessValidator < ActiveModel::Validator - def validate(record) - if record.mirror? && (record.private_rw? || record.public_rw?) - record.errors[:access] = - 'Error! Write access is not allowed for a mirrored repositry.' - end - end + inclusion: {in: Repository::Access::OPTIONS}, + unless: :mirror? + validates :access, + presence: true, + inclusion: {in: Repository::Access::OPTIONS_MIRROR}, + if: :mirror? end class UnreservedValidator < ActiveModel::Validator From 0c13daae99f7290dc5fdcc65f00fcef5ca1d8cbe Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 21:24:39 +0100 Subject: [PATCH 166/240] Split options for mirror and non-mirror repos. --- app/helpers/repositories_helper.rb | 12 +++++------- config/locales/en.yml | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 8c2fa5b0d..964ebac89 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -34,13 +34,11 @@ def access_change_hint end def access_options - t('repository.access.options').select do |k,v| - if @repository.mirror? - k.to_s.split('_')[1] == 'r' - else - true - end - end.invert + t('repository.access.options').invert + end + + def access_options_mirror + t('repository.access.options_mirror').invert end def repository_modal_body diff --git a/config/locales/en.yml b/config/locales/en.yml index 72c0fc9f2..55a3c0b35 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -63,10 +63,12 @@ en: access: change_hint: 'Caution! When setting the repository public, all reader permissions are removed.' options: - private_r: Privately readable, not writable at all private_rw: Privately readable, privately writable public_r: Publicly readable, privately writable public_rw: Publicly readable, publicly writable + options_mirror: + private_r: Privately readable, not writable at all + public_r: Publicly readable, not writable at all remote_type: text: A mirror will be synchronized periodically. A fork is just a one-time copy. un_mirror: From 73894395cf3d3ce7ebeeb4fb3bd83996a6733c18 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 21:26:34 +0100 Subject: [PATCH 167/240] Change view and js to use split repo access options. --- .../repository/edit_remote.js.coffee | 23 +++++++++++-------- app/views/repositories/_form.html.haml | 5 +++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/repository/edit_remote.js.coffee b/app/assets/javascripts/repository/edit_remote.js.coffee index 51d9b77b0..079841533 100644 --- a/app/assets/javascripts/repository/edit_remote.js.coffee +++ b/app/assets/javascripts/repository/edit_remote.js.coffee @@ -1,16 +1,21 @@ $ -> if $('#repository_form').length > 0 - mirror_option = (o) -> o.value.search(/_rw/) == -1 - to_mirror_option = (value) -> value.split("_")[0] + "_r" - to_non_mirror_option = (value) -> value - input_access_el = $('#repository_form #repository_access')[0] - source_address_el = $('#repository_form #repository_source_address') - options_all = input_access_el.options - options_non_mirror = _.clone(options_all) - options_mirror = _.filter(options_all, mirror_option) + to_mirror_option = (previous_option) -> + previous_option.split("_")[0] + "_r" - type_el = $($('#repository_form #remote_type')[0]) + to_non_mirror_option = (previous_option) -> + if previous_option.split('_')[0] == 'public' + previous_option + else + 'private_rw' + + input_access_el = $('#repository_access')[0] + source_address_el = $('#repository_source_address') + options_non_mirror = _.clone($('#access_options_non_mirror')[0].options) + options_mirror = _.clone($('#access_options_mirror')[0].options) + + type_el = $($('#remote_type')[0]) # If options are not set one by one, the select box wont change. set_options = (options, converter, last_value) -> diff --git a/app/views/repositories/_form.html.haml b/app/views/repositories/_form.html.haml index bad2b8776..a6e7cb84d 100644 --- a/app/views/repositories/_form.html.haml +++ b/app/views/repositories/_form.html.haml @@ -1,7 +1,10 @@ = simple_form_for resource, html: {class: 'form-horizontal', id: 'repository_form' } do |f| = f.input :name, input_html: { class: 'input-xlarge' }, required: true, disabled: resource.persisted?, as: :string = f.input :description, input_html: { rows: 8 } - = f.input :access, collection: access_options, include_blank: false, hint: access_change_hint + = f.input :access, collection: resource.mirror? ? access_options_mirror : access_options, include_blank: false, hint: access_change_hint + .access-options.hide + = select_tag :access_options_non_mirror, options_for_select(access_options) + = select_tag :access_options_mirror, options_for_select(access_options_mirror) - if resource.new_record? = f.input :source_address, as: :url From 8cb89db2406324b7f09d5a83b39eb14b2623150e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 22:02:37 +0100 Subject: [PATCH 168/240] Remove specs that test private_r. --- spec/models/ability_spec.rb | 36 +++++++++++++++++---------- spec/models/repository/access_spec.rb | 2 -- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 956c5381e..45d042b35 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -172,10 +172,10 @@ end end - context 'Private read-only Repository' do - let(:editor){ create :user } # editor - let(:reader){ create :user } # reader - let(:item){ create(:repository, access: 'private_r', user: owner) } + context 'Private Repository' do + let(:editor) { create :user } # editor + let(:reader) { create :user } # reader + let(:item) { create(:repository, access: 'private_rw', user: owner) } before do create(:permission, subject: editor, role: 'editor', item: item) @@ -192,29 +192,39 @@ end end - context 'reader, editor, owner' do + context 'reader' do it 'not be allowed: to write' do - [reader, editor, owner].each do |role| - Ability.new(role, nil).should_not be_able_to(:write, item) + expect(Ability.new(reader, nil)).to_not be_able_to(:write, item) + end + + it 'be allowed: to read' do + expect(Ability.new(reader, nil)).to be_able_to(:show, item) + end + end + + context 'editor, owner' do + it 'be allowed: to write' do + [editor, owner].each do |role| + expect(Ability.new(role, nil)).to be_able_to(:write, item) end end it 'be allowed: to read' do - [reader, editor, owner].each do |role| - Ability.new(role, nil).should be_able_to(:show, item) + [editor, owner].each do |role| + expect(Ability.new(role, nil)).to be_able_to(:show, item) end end end context 'update:' do - it 'reader, editor should be allowed' do + it 'reader, editor should not be allowed' do [reader, editor].each do |role| - Ability.new(role, nil).should_not be_able_to(:update, item) + expect(Ability.new(role, nil)).to_not be_able_to(:update, item) end end - it 'owner should not be allowed' do - Ability.new(owner, nil).should be_able_to(:update, item) + it 'owner should be allowed' do + expect(Ability.new(owner, nil)).to be_able_to(:update, item) end end end diff --git a/spec/models/repository/access_spec.rb b/spec/models/repository/access_spec.rb index dc1c63c5e..76575906f 100644 --- a/spec/models/repository/access_spec.rb +++ b/spec/models/repository/access_spec.rb @@ -7,8 +7,6 @@ access: 'public_r' } let!(:repository_pub_rw) { create :repository, user: user, access: 'public_rw' } - let!(:repository_priv_r) { create :repository, user: user, - access: 'private_r' } let!(:repository_priv_rw) { create :repository, user: user, access: 'private_rw' } From bf4f06af898b6e444231e8f8212c0a4dded7bdd8 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:16:38 +0100 Subject: [PATCH 169/240] Fix permissions selector for forks. --- .../repository/edit_remote.js.coffee | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/repository/edit_remote.js.coffee b/app/assets/javascripts/repository/edit_remote.js.coffee index 079841533..89ac1629d 100644 --- a/app/assets/javascripts/repository/edit_remote.js.coffee +++ b/app/assets/javascripts/repository/edit_remote.js.coffee @@ -12,6 +12,9 @@ $ -> input_access_el = $('#repository_access')[0] source_address_el = $('#repository_source_address') + remote_type_els = $('input[name="repository[remote_type]"]') + remote_type_mirror_el = $('#repository_remote_type_mirror') + options_non_mirror = _.clone($('#access_options_non_mirror')[0].options) options_mirror = _.clone($('#access_options_mirror')[0].options) @@ -31,20 +34,28 @@ $ -> else set_options(options_non_mirror, to_non_mirror_option, last_value) + is_mirror_selected = () -> + remote_type_mirror_el.is(':checked') + is_source_address_set = () -> source_address_el.val().trim().length > 0 + was_mirror_selected = is_mirror_selected() + was_source_address_set = is_source_address_set() + reset_mirror_selected = () -> + was_mirror_selected = is_mirror_selected() + reset_was_source_address_set = () -> was_source_address_set = is_source_address_set() change_options = (event) -> - if !was_source_address_set && is_source_address_set() + if !was_mirror_selected && is_mirror_selected() modify_access_list 'mirror' - else if was_source_address_set && !is_source_address_set() + else if was_mirror_selected && !is_mirror_selected() modify_access_list 'non_mirror' - else if !was_source_address_set && !is_source_address_set() + else if !was_mirror_selected && !is_mirror_selected() modify_access_list 'non_mirror' change_type_display = (event) -> @@ -60,7 +71,9 @@ $ -> change_options() change_type_display() - source_address_el.on('input', change_options) source_address_el.on('input', change_type_display) source_address_el.on('input', reset_was_source_address_set) + _.each(remote_type_els, (el) -> + $(el).on('click', change_options) + ) return From 2c8b126b36cb4c64708fd31bc47125c097b192ba Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:32:05 +0100 Subject: [PATCH 170/240] Move access select box down. --- app/views/repositories/_form.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/repositories/_form.html.haml b/app/views/repositories/_form.html.haml index a6e7cb84d..e919a031a 100644 --- a/app/views/repositories/_form.html.haml +++ b/app/views/repositories/_form.html.haml @@ -1,10 +1,6 @@ = simple_form_for resource, html: {class: 'form-horizontal', id: 'repository_form' } do |f| = f.input :name, input_html: { class: 'input-xlarge' }, required: true, disabled: resource.persisted?, as: :string = f.input :description, input_html: { rows: 8 } - = f.input :access, collection: resource.mirror? ? access_options_mirror : access_options, include_blank: false, hint: access_change_hint - .access-options.hide - = select_tag :access_options_non_mirror, options_for_select(access_options) - = select_tag :access_options_mirror, options_for_select(access_options_mirror) - if resource.new_record? = f.input :source_address, as: :url @@ -19,4 +15,9 @@ = check_box_tag :un_mirror = t 'repository.un_mirror.text' + = f.input :access, collection: resource.mirror? ? access_options_mirror : access_options, include_blank: false, hint: access_change_hint + .access-options.hide + = select_tag :access_options_non_mirror, options_for_select(access_options) + = select_tag :access_options_mirror, options_for_select(access_options_mirror) + = f.button :wrapped From 2bc34aed84d8bdffef780bb4ae6ce431a7ac5270 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:32:25 +0100 Subject: [PATCH 171/240] Add hint that the access may change. --- config/locales/en.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 55a3c0b35..864ef9bb8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -70,7 +70,9 @@ en: private_r: Privately readable, not writable at all public_r: Publicly readable, not writable at all remote_type: - text: A mirror will be synchronized periodically. A fork is just a one-time copy. + text: + Changing this value may change the access as well. + A mirror will be synchronized periodically. A fork is just a one-time copy. un_mirror: label: Un-mirror text: If you un-mirror this repository, it won't get synchronized with the remote reposoitory any more. There is no possibility to synchronize it ever again. From 79c84e7439066dd27ecbdab0ecd1379141d7ad1b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:32:57 +0100 Subject: [PATCH 172/240] Fix validation not executing for a fork. --- app/models/repository/importing.rb | 4 ++++ app/models/repository/validations.rb | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/repository/importing.rb b/app/models/repository/importing.rb index 4c6c04d1c..1c59c97fd 100644 --- a/app/models/repository/importing.rb +++ b/app/models/repository/importing.rb @@ -30,6 +30,10 @@ def fork? remote_type == 'fork' end + def remote? + REMOTE_TYPES.include?(remote_type) + end + def convert_to_local! self.remote_type = 'fork' save! diff --git a/app/models/repository/validations.rb b/app/models/repository/validations.rb index a48744437..cb87cee76 100644 --- a/app/models/repository/validations.rb +++ b/app/models/repository/validations.rb @@ -113,9 +113,10 @@ def validate(record) class SourceTypeValidator < ActiveModel::Validator def validate(record) - if record.mirror? && !record.source_type.present? + if record.remote? && !record.source_type.present? record.errors[:source_address] = 'not a valid remote repository '\ - "or not accessible (types supported: #{SOURCE_TYPES.join(', ')})" + 'or not accessible '\ + "(types supported: #{Repository::SOURCE_TYPES.join(', ')})" record.errors[:source_type] = 'not present' end end From d3b7d51627bb5f766d723afaed8cb9f14482892a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 16:42:59 +0100 Subject: [PATCH 173/240] Style fixes. --- app/assets/javascripts/repository/edit_remote.js.coffee | 8 ++++---- app/models/repository/access.rb | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/repository/edit_remote.js.coffee b/app/assets/javascripts/repository/edit_remote.js.coffee index 89ac1629d..4e0280964 100644 --- a/app/assets/javascripts/repository/edit_remote.js.coffee +++ b/app/assets/javascripts/repository/edit_remote.js.coffee @@ -34,20 +34,20 @@ $ -> else set_options(options_non_mirror, to_non_mirror_option, last_value) - is_mirror_selected = () -> + is_mirror_selected = -> remote_type_mirror_el.is(':checked') - is_source_address_set = () -> + is_source_address_set = -> source_address_el.val().trim().length > 0 was_mirror_selected = is_mirror_selected() was_source_address_set = is_source_address_set() - reset_mirror_selected = () -> + reset_mirror_selected = -> was_mirror_selected = is_mirror_selected() - reset_was_source_address_set = () -> + reset_was_source_address_set = -> was_source_address_set = is_source_address_set() change_options = (event) -> diff --git a/app/models/repository/access.rb b/app/models/repository/access.rb index c53c010c5..71e640f7f 100644 --- a/app/models/repository/access.rb +++ b/app/models/repository/access.rb @@ -1,8 +1,8 @@ module Repository::Access extend ActiveSupport::Concern - OPTIONS = %w[public_r public_rw private_rw] - OPTIONS_MIRROR = %w[public_r private_r] + OPTIONS = %w(public_r public_rw private_rw) + OPTIONS_MIRROR = %w(public_r private_r) DEFAULT_OPTION = OPTIONS[0] def self.as_read_only(access) From d30259e99ac8e7afd42077b99d3cd971c50c791c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:06:05 +0100 Subject: [PATCH 174/240] Allow to pass options to paginator rendering helper. --- app/helpers/pagination_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index 17c4a41bd..029ceeefc 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -1,10 +1,10 @@ module PaginationHelper - def pagination(collection=nil, &block) + def pagination(collection=nil, **options, &block) # call the collection-method if no collection is given collection ||= send :collection - pages = paginate(collection) + pages = paginate(collection, **options) html = '' html << pages From 1a9a80e830ec53eee6e16158f16ce4427d7492d6 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:06:57 +0100 Subject: [PATCH 175/240] Pass option to build links from request to paginator. --- app/views/axioms/index.html.haml | 2 +- app/views/theorems/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/axioms/index.html.haml b/app/views/axioms/index.html.haml index 6f5d8ae5a..b2fd207c4 100644 --- a/app/views/axioms/index.html.haml +++ b/app/views/axioms/index.html.haml @@ -14,7 +14,7 @@ = link_to "Click here #{t('.to_display_all_axioms')}", locid_for(parent, :axioms, all: true) -= pagination do += pagination(build_links_from_request: true) do %table.axioms %thead %tr diff --git a/app/views/theorems/index.html.haml b/app/views/theorems/index.html.haml index 7cd1b72d7..f63fd1348 100644 --- a/app/views/theorems/index.html.haml +++ b/app/views/theorems/index.html.haml @@ -5,7 +5,7 @@ - if can?(:write, parent.repository) && @ontology.theorems.provable.any? = link_to t('theorems.index.prove'), [*resource_chain, :proofs, :new], class: 'btn btn-primary' -= pagination do += pagination(build_links_from_request: true) do %table.sentences %thead %tr From 86e6513d2355dd69a5993812e4651d7d7535aacb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:07:33 +0100 Subject: [PATCH 176/240] Add helper method to build the page-URL from the request-URL. --- app/helpers/pagination_helper.rb | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index 029ceeefc..bac6766b6 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -13,4 +13,44 @@ def pagination(collection=nil, **options, &block) html.html_safe end + # Kaminari generates a URL from the params hash. The URL is, however, not + # compatible to loc/ids. + # This method builds the loc/id compatible link to the page. + def build_link_from_request(kaminari_url) + page, per_page = params_from_kaminari_url(kaminari_url) + + query_string_parts = request.env['QUERY_STRING']. + split(/;|&/). + map { |p| p.split('=') } + + query_string_parts.reject! { |p| p.first == 'page' } if page + query_string_parts.reject! { |p| p.first == 'per_page' } if per_page + + query_string_parts << ['page', page] if page + query_string_parts << ['per_page', per_page] if per_page && per_page != :default + + query_string_parts.map! { |p| p.join('=') } + query_string = query_string_parts.join('&') + + [request.env['REQUEST_PATH'], query_string].compact.join('?') + end + + # Kaminari generates a URL from the params hash. The URL is, however, not + # compatible to loc/ids. + # This method extracts the page number and page size from the generated url. + def params_from_kaminari_url(kaminari_url) + page = + if match = kaminari_url.match(/[\?&]page=(\d+)/) + match[1] + else + 1 + end + per_page = + if match = kaminari_url.match(/[\?&]per_page=(\d+)/) + match[1] + else + nil + end + [page, per_page] + end end From 2018f47ea7348607c091e0abcbd7c25e70ca6d42 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:08:14 +0100 Subject: [PATCH 177/240] Build the page-URL from the request-URL when needed. --- app/views/kaminari/_first_page.html.haml | 7 ++++++- app/views/kaminari/_last_page.html.haml | 9 +++++++-- app/views/kaminari/_next_page.html.haml | 7 ++++++- app/views/kaminari/_page.html.haml | 7 ++++++- app/views/kaminari/_prev_page.html.haml | 7 ++++++- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/views/kaminari/_first_page.html.haml b/app/views/kaminari/_first_page.html.haml index 3fc9fc1c7..4d3a54fcd 100644 --- a/app/views/kaminari/_first_page.html.haml +++ b/app/views/kaminari/_first_page.html.haml @@ -1,3 +1,8 @@ - unless current_page.first? %li.first - = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote + - if current_page.instance_variable_get(:@options)[:build_links_from_request] + - # use request url for page creation + = link_to_unless current_page.first?, raw(t 'views.pagination.first'), build_link_from_request(url), :remote => remote + - else + - # use default kaminari behavior + = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote diff --git a/app/views/kaminari/_last_page.html.haml b/app/views/kaminari/_last_page.html.haml index 854937c3e..c5f18c957 100644 --- a/app/views/kaminari/_last_page.html.haml +++ b/app/views/kaminari/_last_page.html.haml @@ -1,3 +1,8 @@ - unless current_page.last? - %li.last.next/ - = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} + %li.last.next + - if current_page.instance_variable_get(:@options)[:build_links_from_request] + - # use request url for page creation + = link_to_unless current_page.last?, raw(t 'views.pagination.last'), build_link_from_request(url), {:remote => remote} + - else + - # use default kaminari behavior + = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} diff --git a/app/views/kaminari/_next_page.html.haml b/app/views/kaminari/_next_page.html.haml index 3c933e386..cae7c2a86 100644 --- a/app/views/kaminari/_next_page.html.haml +++ b/app/views/kaminari/_next_page.html.haml @@ -1,3 +1,8 @@ - unless current_page.last? %li.next_page - = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote + - if current_page.instance_variable_get(:@options)[:build_links_from_request] + - # use request url for page creation + = link_to_unless current_page.last?, raw(t 'views.pagination.next'), build_link_from_request(url), :rel => 'next', :remote => remote + - else + - # use default kaminari behavior + = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote diff --git a/app/views/kaminari/_page.html.haml b/app/views/kaminari/_page.html.haml index 63f5a3f01..645c89479 100644 --- a/app/views/kaminari/_page.html.haml +++ b/app/views/kaminari/_page.html.haml @@ -1,2 +1,7 @@ %li{ :class => page.current? ? 'active' : nil } - = link_to page, url, :remote => remote, :rel => (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) + - if current_page.instance_variable_get(:@options)[:build_links_from_request] + - # use request url for page creation + = link_to page, build_link_from_request(url), :remote => remote, :rel => (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) + - else + - # use default kaminari behavior + = link_to page, url, :remote => remote, :rel => (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) diff --git a/app/views/kaminari/_prev_page.html.haml b/app/views/kaminari/_prev_page.html.haml index 47c00bd61..a0c6de0f7 100644 --- a/app/views/kaminari/_prev_page.html.haml +++ b/app/views/kaminari/_prev_page.html.haml @@ -1,3 +1,8 @@ - unless current_page.first? %li.prev - = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote + - if current_page.instance_variable_get(:@options)[:build_links_from_request] + - # use request url for page creation + = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), build_link_from_request(url), :rel => 'prev', :remote => remote + - else + - # use default kaminari behavior + = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote From 1a6844d128baadfc04abc4c98f5dd23113cff7ff Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:08:35 +0100 Subject: [PATCH 178/240] Add per_page support for axioms index. --- app/controllers/axioms_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/axioms_controller.rb b/app/controllers/axioms_controller.rb index bf50ad030..be1b70750 100644 --- a/app/controllers/axioms_controller.rb +++ b/app/controllers/axioms_controller.rb @@ -27,9 +27,9 @@ def collection else parent.translated_axioms end - Kaminari.paginate_array(axioms).page(params[:page]) + Kaminari.paginate_array(axioms).page(params[:page]).per(params[:per_page]) else - Kaminari.paginate_array(parent.axioms.original).page(params[:page]) + Kaminari.paginate_array(parent.axioms.original).page(params[:page]).per(params[:per_page]) end end From 4bb9b23b5c77c38236dabb67699326915e7edc1b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 4 Jan 2016 20:16:01 +0100 Subject: [PATCH 179/240] Make "per page" one-lined. --- app/views/kaminari/_paginator.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/kaminari/_paginator.html.haml b/app/views/kaminari/_paginator.html.haml index 2ec8a0d63..c3e2950f2 100644 --- a/app/views/kaminari/_paginator.html.haml +++ b/app/views/kaminari/_paginator.html.haml @@ -20,7 +20,7 @@ = last_page_tag unless current_page.last? || exclude.include?(:last) - unless exclude.include?(:per_page) %form.row - %label + %label.col-md-3 .col-md-6 %select{name: 'per_page', class: 'form-control'} - [25, 50, 100, 500].each do |num| From 3ae631255aa487144a2df13c75215e402d5e97a1 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:49:27 +0100 Subject: [PATCH 180/240] Fix symbols index pagination. --- app/views/symbols/index.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/symbols/index.html.haml b/app/views/symbols/index.html.haml index 6bb9c3317..54db56559 100644 --- a/app/views/symbols/index.html.haml +++ b/app/views/symbols/index.html.haml @@ -3,7 +3,7 @@ = ontology_nav parent, :symbols - unless parent.owl? - = pagination do + = pagination(build_links_from_request: true) do - unless collection.blank? = render partial: 'list' - else @@ -25,7 +25,7 @@ = render partial: 'treeview' .symbols-detail - = pagination do + = pagination(build_links_from_request: true) do - unless collection.blank? = render partial: 'list' - else From cf024cc949ef1a0ab1e382f304507cb589f6f0a9 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Mon, 11 Jan 2016 15:57:28 +0100 Subject: [PATCH 181/240] Remove unused view: ontologies/show. --- app/views/ontologies/show.html.haml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 app/views/ontologies/show.html.haml diff --git a/app/views/ontologies/show.html.haml b/app/views/ontologies/show.html.haml deleted file mode 100644 index e9c244bd5..000000000 --- a/app/views/ontologies/show.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -= ontology_nav @ontology, :symbols - -= pagination do - = render :partial => 'symbols/index' From 3f99e95dd4a99d2ab79a50aa8d72d8cd92cd2ea1 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:34:10 +0100 Subject: [PATCH 182/240] Fix code style. --- app/views/kaminari/_first_page.html.haml | 4 ++-- app/views/kaminari/_last_page.html.haml | 4 ++-- app/views/kaminari/_next_page.html.haml | 4 ++-- app/views/kaminari/_page.html.haml | 4 ++-- app/views/kaminari/_prev_page.html.haml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/kaminari/_first_page.html.haml b/app/views/kaminari/_first_page.html.haml index 4d3a54fcd..62b138d77 100644 --- a/app/views/kaminari/_first_page.html.haml +++ b/app/views/kaminari/_first_page.html.haml @@ -1,8 +1,8 @@ - unless current_page.first? %li.first - if current_page.instance_variable_get(:@options)[:build_links_from_request] - - # use request url for page creation + -# use request url for page creation = link_to_unless current_page.first?, raw(t 'views.pagination.first'), build_link_from_request(url), :remote => remote - else - - # use default kaminari behavior + -# use default kaminari behavior = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote diff --git a/app/views/kaminari/_last_page.html.haml b/app/views/kaminari/_last_page.html.haml index c5f18c957..8ce5825ca 100644 --- a/app/views/kaminari/_last_page.html.haml +++ b/app/views/kaminari/_last_page.html.haml @@ -1,8 +1,8 @@ - unless current_page.last? %li.last.next - if current_page.instance_variable_get(:@options)[:build_links_from_request] - - # use request url for page creation + -# use request url for page creation = link_to_unless current_page.last?, raw(t 'views.pagination.last'), build_link_from_request(url), {:remote => remote} - else - - # use default kaminari behavior + -# use default kaminari behavior = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} diff --git a/app/views/kaminari/_next_page.html.haml b/app/views/kaminari/_next_page.html.haml index cae7c2a86..0b723e4a3 100644 --- a/app/views/kaminari/_next_page.html.haml +++ b/app/views/kaminari/_next_page.html.haml @@ -1,8 +1,8 @@ - unless current_page.last? %li.next_page - if current_page.instance_variable_get(:@options)[:build_links_from_request] - - # use request url for page creation + -# use request url for page creation = link_to_unless current_page.last?, raw(t 'views.pagination.next'), build_link_from_request(url), :rel => 'next', :remote => remote - else - - # use default kaminari behavior + -# use default kaminari behavior = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote diff --git a/app/views/kaminari/_page.html.haml b/app/views/kaminari/_page.html.haml index 645c89479..a2ea743fd 100644 --- a/app/views/kaminari/_page.html.haml +++ b/app/views/kaminari/_page.html.haml @@ -1,7 +1,7 @@ %li{ :class => page.current? ? 'active' : nil } - if current_page.instance_variable_get(:@options)[:build_links_from_request] - - # use request url for page creation + -# use request url for page creation = link_to page, build_link_from_request(url), :remote => remote, :rel => (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) - else - - # use default kaminari behavior + -# use default kaminari behavior = link_to page, url, :remote => remote, :rel => (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) diff --git a/app/views/kaminari/_prev_page.html.haml b/app/views/kaminari/_prev_page.html.haml index a0c6de0f7..c3c87d766 100644 --- a/app/views/kaminari/_prev_page.html.haml +++ b/app/views/kaminari/_prev_page.html.haml @@ -1,8 +1,8 @@ - unless current_page.first? %li.prev - if current_page.instance_variable_get(:@options)[:build_links_from_request] - - # use request url for page creation + -# use request url for page creation = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), build_link_from_request(url), :rel => 'prev', :remote => remote - else - - # use default kaminari behavior + -# use default kaminari behavior = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote From 7ea3e78d7552d1ec2d21efac4e14ace1a20f3975 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 14:57:38 +0100 Subject: [PATCH 183/240] Fix code style. --- app/controllers/axioms_controller.rb | 7 ++++--- app/helpers/pagination_helper.rb | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/axioms_controller.rb b/app/controllers/axioms_controller.rb index be1b70750..ebc2be140 100644 --- a/app/controllers/axioms_controller.rb +++ b/app/controllers/axioms_controller.rb @@ -2,7 +2,6 @@ # Lists axioms of an ontology # class AxiomsController < InheritedResources::Base - belongs_to :ontology actions :index @@ -27,9 +26,11 @@ def collection else parent.translated_axioms end - Kaminari.paginate_array(axioms).page(params[:page]).per(params[:per_page]) + Kaminari.paginate_array(axioms).page(params[:page]). + per(params[:per_page]) else - Kaminari.paginate_array(parent.axioms.original).page(params[:page]).per(params[:per_page]) + Kaminari.paginate_array(parent.axioms.original).page(params[:page]). + per(params[:per_page]) end end diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index bac6766b6..b26add243 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -1,6 +1,6 @@ module PaginationHelper - def pagination(collection=nil, **options, &block) + def pagination(collection = nil, **options, &block) # call the collection-method if no collection is given collection ||= send :collection @@ -27,7 +27,9 @@ def build_link_from_request(kaminari_url) query_string_parts.reject! { |p| p.first == 'per_page' } if per_page query_string_parts << ['page', page] if page - query_string_parts << ['per_page', per_page] if per_page && per_page != :default + if per_page && per_page != :default + query_string_parts << ['per_page', per_page] + end query_string_parts.map! { |p| p.join('=') } query_string = query_string_parts.join('&') @@ -48,8 +50,6 @@ def params_from_kaminari_url(kaminari_url) per_page = if match = kaminari_url.match(/[\?&]per_page=(\d+)/) match[1] - else - nil end [page, per_page] end From 2c941942e8229fe0cc6102f261303845a899f823 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 15:08:37 +0100 Subject: [PATCH 184/240] Shorten methods. --- app/helpers/pagination_helper.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index b26add243..179a09117 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -31,8 +31,7 @@ def build_link_from_request(kaminari_url) query_string_parts << ['per_page', per_page] end - query_string_parts.map! { |p| p.join('=') } - query_string = query_string_parts.join('&') + query_string = query_string_parts.map { |p| p.join('=') }.join('&') [request.env['REQUEST_PATH'], query_string].compact.join('?') end @@ -42,15 +41,13 @@ def build_link_from_request(kaminari_url) # This method extracts the page number and page size from the generated url. def params_from_kaminari_url(kaminari_url) page = - if match = kaminari_url.match(/[\?&]page=(\d+)/) + if (match = kaminari_url.match(/[\?&]page=(\d+)/)) ? match[1] : 1 match[1] - else - 1 end per_page = if match = kaminari_url.match(/[\?&]per_page=(\d+)/) match[1] end - [page, per_page] + [page || 1, per_page] end end From 5b5a719ebc902d7e2bacd575a4f80816c02594be Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 15:54:36 +0100 Subject: [PATCH 185/240] More style fixes. --- app/helpers/pagination_helper.rb | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index 179a09117..4268a1bc1 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -1,5 +1,4 @@ module PaginationHelper - def pagination(collection = nil, **options, &block) # call the collection-method if no collection is given collection ||= send :collection @@ -23,15 +22,8 @@ def build_link_from_request(kaminari_url) split(/;|&/). map { |p| p.split('=') } - query_string_parts.reject! { |p| p.first == 'page' } if page - query_string_parts.reject! { |p| p.first == 'per_page' } if per_page - - query_string_parts << ['page', page] if page - if per_page && per_page != :default - query_string_parts << ['per_page', per_page] - end - - query_string = query_string_parts.map { |p| p.join('=') }.join('&') + query_string_parts = replace_page_params(query_string_parts, page, per_page) + query_string = build_query_string(query_string_parts) [request.env['REQUEST_PATH'], query_string].compact.join('?') end @@ -50,4 +42,23 @@ def params_from_kaminari_url(kaminari_url) end [page || 1, per_page] end + + private + + def replace_page_params(page, per_page) + query_string_parts.reject! { |p| p.first == 'page' } if page + query_string_parts.reject! { |p| p.first == 'per_page' } if per_page + + query_string_parts << ['page', page] if page + if per_page && per_page != :default + query_string_parts << ['per_page', per_page] + end + + query_string_parts + end + + def build_query_string(query_string_parts) + query_string = query_string_parts.map { |p| p.join('=') }.join('&') + query_string if query_string.present? + end end From 2e0ea9e168a66f3e6d73be8b41f2847d9c0f02eb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 21:54:00 +0100 Subject: [PATCH 186/240] Refactor AsyncHelper: Add and use OopsRequestWorker. --- app/models/oops_request/states.rb | 4 +++- lib/oops_request_worker.rb | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 lib/oops_request_worker.rb diff --git a/app/models/oops_request/states.rb b/app/models/oops_request/states.rb index f8713899f..b24f32b6b 100644 --- a/app/models/oops_request/states.rb +++ b/app/models/oops_request/states.rb @@ -11,7 +11,6 @@ module OopsRequest::States include StateUpdater included do - async_method :run after_create :async_run, if: ->{ responses.empty? } end @@ -24,4 +23,7 @@ def run end end + def async_run + OopsRequestWorker.perform_async(id) + end end diff --git a/lib/oops_request_worker.rb b/lib/oops_request_worker.rb new file mode 100644 index 000000000..5b2d8a70d --- /dev/null +++ b/lib/oops_request_worker.rb @@ -0,0 +1,7 @@ +class OopsRequestWorker < BaseWorker + sidekiq_options retry: 3, queue: 'default' + + def perform(oops_request_id) + OopsRequest.find(oops_request_id).run + end +end From aa5b830a4466ff1de5ef1bc50a19c01a6c8a01ea Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 21:54:46 +0100 Subject: [PATCH 187/240] Refactor AsyncHelper: Remove unused async_method. --- config/initializers/async_helper.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/config/initializers/async_helper.rb b/config/initializers/async_helper.rb index 8a9cfe377..1c1050c3b 100644 --- a/config/initializers/async_helper.rb +++ b/config/initializers/async_helper.rb @@ -1,15 +1,4 @@ class ActiveRecord::Base - - # Creates a async_ method that - # enqueues the corresponding method call. - def self.async_method(*methods) - methods.each do |method| - define_method "async_#{method}" do |*args| - async method, *args - end - end - end - def self.async(method, *args) async_in 'class', 1, method.to_s, *args end @@ -29,5 +18,4 @@ def self.async_in(type, at, *args) 'args' => [type, self.to_s, *args], 'at' => (at < 1_000_000_000 ? Time.now + at : at).to_f end - end From c975e932187f329ac00049d8fd7cc796633f9e92 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 22:14:08 +0100 Subject: [PATCH 188/240] Refactor AsyncHelper: Add RepositoyFetchingWorker. --- app/models/repository/importing.rb | 4 ++-- lib/repository_fetching_worker.rb | 10 ++++++++++ spec/lib/repository/import/git_spec.rb | 4 ++-- spec/lib/repository/import/svn_spec.rb | 6 +++--- spec/models/repository/git_spec.rb | 4 ++-- 5 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 lib/repository_fetching_worker.rb diff --git a/app/models/repository/importing.rb b/app/models/repository/importing.rb index 4c6c04d1c..de00ad5ce 100644 --- a/app/models/repository/importing.rb +++ b/app/models/repository/importing.rb @@ -44,11 +44,11 @@ def locked? def async_remote(method) raise "object is #{state}" if locked? update_state! 'pending' - async :remote_send, method, remote_type + RepositoryFetchingWorker.perform_async(id, method, remote_type) end # executes a pull/clone job - def remote_send(method, remote_type = nil) + def fetch(method, remote_type = nil) # build arguments args = [] args << source_address if method == 'clone' diff --git a/lib/repository_fetching_worker.rb b/lib/repository_fetching_worker.rb new file mode 100644 index 000000000..e7a9fc198 --- /dev/null +++ b/lib/repository_fetching_worker.rb @@ -0,0 +1,10 @@ +class RepositoryFetchingWorker < BaseWorker + sidekiq_options retry: false, queue: 'default' + + # method can be "clone" or "pull" + # remote_type can be nil (which is for git) or "svn" + def perform(repository_id, method, remote_type) + Repository.find(repository_id).fetch(method, remote_type) + end +end + diff --git a/spec/lib/repository/import/git_spec.rb b/spec/lib/repository/import/git_spec.rb index 530c0273b..3638f02d6 100644 --- a/spec/lib/repository/import/git_spec.rb +++ b/spec/lib/repository/import/git_spec.rb @@ -17,7 +17,7 @@ remote_type: 'mirror') end - before { Worker.drain } + before { RepositoryFetchingWorker.drain } it 'detect that it is not an svn repo' do expect(GitRepository.is_svn_repository?(remote_repo.path)).to be_falsy @@ -62,7 +62,7 @@ remote_repo.commit_file(userinfo, "(#{m})", "file-#{m}.clif", "add #{m}") end end - let!(:result) { repository.remote_send :pull } + let!(:result) { repository.fetch(:pull) } context 'get the new changes' do let!(:head_oid_post) { remote_repo.head_oid } diff --git a/spec/lib/repository/import/svn_spec.rb b/spec/lib/repository/import/svn_spec.rb index c9a16266f..685c812d1 100644 --- a/spec/lib/repository/import/svn_spec.rb +++ b/spec/lib/repository/import/svn_spec.rb @@ -35,7 +35,7 @@ def commit(work_dir, count, prefix) before do commit(work_dir, commit_count, '') - Worker.drain + RepositoryFetchingWorker.drain end @@ -52,7 +52,7 @@ def commit(work_dir, count, prefix) end context 'synchronizing without new commits' do - let!(:result) { repository.remote_send :pull } + let!(:result) { repository.fetch(:pull) } it 'unchanged HEAD' do expect(result.current).to eq(result.previous) @@ -62,7 +62,7 @@ def commit(work_dir, count, prefix) context 'adding commits to svn' do before { commit(work_dir, commit_count, 'new_') } - let!(:result) { repository.remote_send :pull } + let!(:result) { repository.fetch(:pull) } it 'get the new commits' do expect(repository.walk_commits.size).to eq(commit_count * 2) diff --git a/spec/models/repository/git_spec.rb b/spec/models/repository/git_spec.rb index f2f21004e..53120bb49 100644 --- a/spec/models/repository/git_spec.rb +++ b/spec/models/repository/git_spec.rb @@ -14,12 +14,12 @@ end it 'should create a bulk job on a queue' do - expect { repository.remote_send('clone') }. + expect { repository.fetch('clone') }. to change(OntologyBatchParseWorker.jobs, :size) end it 'should run the ontology import on the cloned repository', process_jobs_synchronously: true do - expect { repository.remote_send('clone') }. + expect { repository.fetch('clone') }. not_to raise_error end end From 56f97c5af9f86663f034049274ae16642c5ba4df Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 6 Jan 2016 22:17:07 +0100 Subject: [PATCH 189/240] Refactor AsyncHelper: Restructure SidetiqWorker to RepositoyPullingWorker. --- app/models/repository/importing.rb | 4 ++-- lib/repository_pulling_worker.rb | 12 ++++++++++++ lib/sidetiq_worker.rb | 16 ---------------- ...spec.rb => repository_pulling_worker_spec.rb} | 10 +++++----- 4 files changed, 19 insertions(+), 23 deletions(-) create mode 100644 lib/repository_pulling_worker.rb delete mode 100644 lib/sidetiq_worker.rb rename spec/lib/{sidetiq_worker_spec.rb => repository_pulling_worker_spec.rb} (71%) diff --git a/app/models/repository/importing.rb b/app/models/repository/importing.rb index de00ad5ce..33d3fdc6a 100644 --- a/app/models/repository/importing.rb +++ b/app/models/repository/importing.rb @@ -19,7 +19,7 @@ module Repository::Importing include StateUpdater before_validation :clean_and_initialize_record - after_create ->() { async_remote :clone }, if: :source_address? + after_create ->() { call_remote :clone }, if: :source_address? end def mirror? @@ -41,7 +41,7 @@ def locked? end # enqueues a pull/clone job - def async_remote(method) + def call_remote(method) raise "object is #{state}" if locked? update_state! 'pending' RepositoryFetchingWorker.perform_async(id, method, remote_type) diff --git a/lib/repository_pulling_worker.rb b/lib/repository_pulling_worker.rb new file mode 100644 index 000000000..8db5335cc --- /dev/null +++ b/lib/repository_pulling_worker.rb @@ -0,0 +1,12 @@ +class RepositoryPullingWorker + include Sidekiq::Worker + include Sidetiq::Schedulable + + sidekiq_options retry: false, queue: 'default' + + recurrence { minutely(5) } + + def perform + Repository.outdated.find_each { |r| r.call_remote :pull } + end +end diff --git a/lib/sidetiq_worker.rb b/lib/sidetiq_worker.rb deleted file mode 100644 index 9b8bb04dd..000000000 --- a/lib/sidetiq_worker.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'sidekiq/worker' - -# This worker creates remote_pull jobs -# to update imported repositories. -class SidetiqWorker - - include Sidekiq::Worker - include Sidetiq::Schedulable - - recurrence { minutely(5) } - - def perform - Repository.outdated.find_each{|r| r.async_remote :pull } - end - -end diff --git a/spec/lib/sidetiq_worker_spec.rb b/spec/lib/repository_pulling_worker_spec.rb similarity index 71% rename from spec/lib/sidetiq_worker_spec.rb rename to spec/lib/repository_pulling_worker_spec.rb index a6e50a484..b2aff045d 100644 --- a/spec/lib/sidetiq_worker_spec.rb +++ b/spec/lib/repository_pulling_worker_spec.rb @@ -1,20 +1,20 @@ require 'spec_helper' -describe SidetiqWorker do +describe RepositoryPullingWorker do subject! { create :repository_with_empty_remote } - before{ Worker.clear } + before { RepositoryFetchingWorker.clear } shared_examples 'perform' do |state, minutes, created_jobs_count| - describe "state #{state}, imported " << (minutes ? "#{minutes} minutes ago" : "never before") do + context "state #{state}, imported " << (minutes ? "#{minutes} minutes ago" : "never before") do before do subject.update_attributes!( {state: state.to_s, imported_at: (minutes ? minutes.minutes.ago : nil)}, {without_protection: true} ) - SidetiqWorker.new.perform + RepositoryPullingWorker.new.perform end it("should create #{created_jobs_count} jobs") do - expect(created_jobs_count).to eq(Worker.jobs.count) + expect(RepositoryFetchingWorker.jobs.count).to eq(created_jobs_count) end end end From b16b327fae1e56eea50d41b1477994033838e3c2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 08:21:25 +0100 Subject: [PATCH 190/240] Refactor AsyncHelper: Add RepositoyConversionWorker. --- lib/dsl/repo.rb | 2 +- lib/repository_conversion_worker.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 lib/repository_conversion_worker.rb diff --git a/lib/dsl/repo.rb b/lib/dsl/repo.rb index 0aa1d1f43..8acf7c07e 100644 --- a/lib/dsl/repo.rb +++ b/lib/dsl/repo.rb @@ -61,6 +61,6 @@ def save r.user = User.first r.url_maps = @url_maps r.save! - r.async :convert_to_local! + RepositoryCeonversionWorker.perform_async(r.id) end end diff --git a/lib/repository_conversion_worker.rb b/lib/repository_conversion_worker.rb new file mode 100644 index 000000000..666daae17 --- /dev/null +++ b/lib/repository_conversion_worker.rb @@ -0,0 +1,10 @@ +class RepositoryConversionWorker < BaseWorker + sidekiq_options retry: false, queue: 'default' + + # method can be "clone" or "pull" + # remote_type can be nil (which is for git) or "svn" + def perform(repository_id) + Repository.find(repository_id).convert_to_local! + end +end + From 49f4112620975dae84bcd13f02667b00f16a3448 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 08:49:15 +0100 Subject: [PATCH 191/240] Refactor AsyncHelper: Add OntologyParsingWorker. --- app/models/ontology_version/parsing.rb | 6 ++++-- lib/ontology_parsing_worker.rb | 14 ++++++++++++++ script/import-ontologies | 2 +- spec/models/ontology_version/parsing_spec.rb | 14 +++++++------- spec/models/repository/jobcount_spec.rb | 6 +++--- spec/models/repository/retry_failed_spec.rb | 4 ++-- .../models/repository/save_and_delete_file_spec.rb | 2 +- 7 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 lib/ontology_parsing_worker.rb diff --git a/app/models/ontology_version/parsing.rb b/app/models/ontology_version/parsing.rb index c8a5f8de4..c90c22116 100644 --- a/app/models/ontology_version/parsing.rb +++ b/app/models/ontology_version/parsing.rb @@ -19,9 +19,11 @@ def async_parse(*args) update_state! :pending if @fast_parse - async :parse_fast, files_to_parse_afterwards + OntologyParsingWorker.perform_async(id, :parse_fast, + files_to_parse_afterwards) else - async :parse_full, files_to_parse_afterwards + OntologyParsingWorker.perform_async(id, :parse_full, + files_to_parse_afterwards) end end end diff --git a/lib/ontology_parsing_worker.rb b/lib/ontology_parsing_worker.rb new file mode 100644 index 000000000..5781d77f9 --- /dev/null +++ b/lib/ontology_parsing_worker.rb @@ -0,0 +1,14 @@ +class OntologyParsingWorker < Worker + sidekiq_options retry: 3, queue: 'hets' + + # parse_mode can be 'parse_fast' or 'parse_full' + # *args only holds the array files_to_parse_afterwards + def perform(ontology_version_id, parse_mode, *args) + # We need to wait a second for the SQL query to finish. + # Otherwise there is no OntologyVersion with id=ontology_version_id. + # FIXME This needs to be handled properly. + sleep 1 unless defined?(Sidekiq::Testing) && Sidekiq::Testing.inline? + super('record', 'OntologyVersion', parse_mode, ontology_version_id, *args, + try_count: 1) + end +end diff --git a/script/import-ontologies b/script/import-ontologies index 508a927b1..b9477c51d 100755 --- a/script/import-ontologies +++ b/script/import-ontologies @@ -56,7 +56,7 @@ ARGV.each do |dir| ov.save! if redis - ov.async :parse + OntologyParsingWorker.perform_async(ov.id, :parse_full) else ov.parse end diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb index 4965f301e..b05be3495 100644 --- a/spec/models/ontology_version/parsing_spec.rb +++ b/spec/models/ontology_version/parsing_spec.rb @@ -7,7 +7,7 @@ before do # Clear Jobs - Worker.jobs.clear + OntologyParsingWorker.jobs.clear stub_hets_for('owl/pizza.owl') end @@ -117,7 +117,7 @@ end it 'should use the locid-ref for calling the parse-caller' do - expect { Worker.drain }.to throw_symbol(:iri, qualified_locid) + expect { OntologyParsingWorker.drain }.to throw_symbol(:iri, qualified_locid) end end @@ -128,7 +128,7 @@ # Run Job # binding.pry ontology_version - Worker.drain + OntologyParsingWorker.drain end it 'should be done' do @@ -154,7 +154,7 @@ before do ontology_version - Worker.drain + OntologyParsingWorker.drain end it 'have sent a request with url-catalog' do @@ -169,7 +169,7 @@ before do allow(Hets).to receive(:parse_via_api).and_raise(Sidekiq::Shutdown) ontology_version - expect { Worker.drain }.to raise_error(Sidekiq::Shutdown) + expect { OntologyParsingWorker.drain }.to raise_error(Sidekiq::Shutdown) end it 'should reset status to pending' do @@ -182,7 +182,7 @@ allow(Hets).to receive(:parse_via_api). and_raise(Hets::HetsError, "serious error") ontology_version - expect { Worker.drain }.to raise_error(Hets::HetsError) + expect { OntologyParsingWorker.drain }.to raise_error(Hets::HetsError) end it 'should set status to failed' do @@ -199,7 +199,7 @@ and_raise(Hets::HetsError, "first error") allow_any_instance_of(OntologyVersion).to receive(:after_failed).and_raise('second exception') ontology_version - expect { Worker.drain }.to raise_error(RuntimeError) + expect { OntologyParsingWorker.drain }.to raise_error(RuntimeError) end it 'should set status to failed on ontology' do diff --git a/spec/models/repository/jobcount_spec.rb b/spec/models/repository/jobcount_spec.rb index 2b7356726..64f4b0db6 100644 --- a/spec/models/repository/jobcount_spec.rb +++ b/spec/models/repository/jobcount_spec.rb @@ -20,7 +20,7 @@ repository.save_file(tmpfile.path, path, message, user) end - expect(Worker.jobs.count).to eq(1) + expect(OntologyParsingWorker.jobs.count).to eq(1) end it 'should not get increased on saving second inroot1 file' do @@ -31,7 +31,7 @@ repository.save_file(tmpfile.path, path, message, user) end - expect(Worker.jobs.count).to eq(1) + expect(OntologyParsingWorker.jobs.count).to eq(1) end it 'should get increased on saving inroot2 file after two inroot1 files' do @@ -42,6 +42,6 @@ repository.save_file(tmpfile.path, path, message, user) end - expect(Worker.jobs.count).to eq(2) + expect(OntologyParsingWorker.jobs.count).to eq(2) end end diff --git a/spec/models/repository/retry_failed_spec.rb b/spec/models/repository/retry_failed_spec.rb index 48126794f..18df67dfc 100644 --- a/spec/models/repository/retry_failed_spec.rb +++ b/spec/models/repository/retry_failed_spec.rb @@ -9,7 +9,7 @@ before { ontology.versions.last.send :update_state!, :failed - Worker.jobs.clear + OntologyParsingWorker.jobs.clear repository.ontologies.retry_failed repository.reload @@ -17,7 +17,7 @@ ontology.reload } - it { Worker.jobs.size.should == 1 } + it { OntologyParsingWorker.jobs.size.should == 1 } it { ontology.state.should == 'pending' } end diff --git a/spec/models/repository/save_and_delete_file_spec.rb b/spec/models/repository/save_and_delete_file_spec.rb index 6490a8846..336b08675 100644 --- a/spec/models/repository/save_and_delete_file_spec.rb +++ b/spec/models/repository/save_and_delete_file_spec.rb @@ -103,7 +103,7 @@ context 'that already exists' do it 'create a job' do expect { repository.save_file(file_path, target_path, message, user) }. - to change { Worker.jobs.count }.by(1) + to change { OntologyParsingWorker.jobs.count }.by(1) end end end From 25209f55886e031f4afa026d557be70948a527b5 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 7 Jan 2016 08:49:46 +0100 Subject: [PATCH 192/240] Refactor AsyncHelper: Remove AsyncHelper. --- config/initializers/async_helper.rb | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 config/initializers/async_helper.rb diff --git a/config/initializers/async_helper.rb b/config/initializers/async_helper.rb deleted file mode 100644 index 1c1050c3b..000000000 --- a/config/initializers/async_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -class ActiveRecord::Base - def self.async(method, *args) - async_in 'class', 1, method.to_s, *args - end - - # Enqueues a method to run in the background - def async(method, *args) - # 1 second in the future should be after finishing the SQL transaction - self.class.async_in 'record', 1, method.to_s, id.to_s, *args - end - - # Enqueues a method to run in the future - def self.async_in(type, at, *args) - Sidekiq::Client.push \ - 'queue' => instance_variable_get('@queue') || 'default', - 'class' => Worker, - 'retry' => false, - 'args' => [type, self.to_s, *args], - 'at' => (at < 1_000_000_000 ? Time.now + at : at).to_f - end -end From 9d3990a5452648f88435496cc73e0d45b63b183e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 13:48:41 +0100 Subject: [PATCH 193/240] Fix typo. --- lib/dsl/repo.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dsl/repo.rb b/lib/dsl/repo.rb index 8acf7c07e..2ff4a06a7 100644 --- a/lib/dsl/repo.rb +++ b/lib/dsl/repo.rb @@ -61,6 +61,6 @@ def save r.user = User.first r.url_maps = @url_maps r.save! - RepositoryCeonversionWorker.perform_async(r.id) + RepositoryConversionWorker.perform_async(r.id) end end From a65018ddb5eac6adba245ceb6d73bbee4956f3e7 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 13:57:33 +0100 Subject: [PATCH 194/240] Fix code style. --- lib/repository_conversion_worker.rb | 1 - lib/repository_fetching_worker.rb | 1 - spec/lib/repository_pulling_worker_spec.rb | 8 +++++--- spec/models/ontology_version/parsing_spec.rb | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/repository_conversion_worker.rb b/lib/repository_conversion_worker.rb index 666daae17..61f4d7676 100644 --- a/lib/repository_conversion_worker.rb +++ b/lib/repository_conversion_worker.rb @@ -7,4 +7,3 @@ def perform(repository_id) Repository.find(repository_id).convert_to_local! end end - diff --git a/lib/repository_fetching_worker.rb b/lib/repository_fetching_worker.rb index e7a9fc198..c9659b0eb 100644 --- a/lib/repository_fetching_worker.rb +++ b/lib/repository_fetching_worker.rb @@ -7,4 +7,3 @@ def perform(repository_id, method, remote_type) Repository.find(repository_id).fetch(method, remote_type) end end - diff --git a/spec/lib/repository_pulling_worker_spec.rb b/spec/lib/repository_pulling_worker_spec.rb index b2aff045d..fe960c027 100644 --- a/spec/lib/repository_pulling_worker_spec.rb +++ b/spec/lib/repository_pulling_worker_spec.rb @@ -5,11 +5,13 @@ before { RepositoryFetchingWorker.clear } shared_examples 'perform' do |state, minutes, created_jobs_count| - context "state #{state}, imported " << (minutes ? "#{minutes} minutes ago" : "never before") do + context("state #{state}, imported " + + (minutes ? "#{minutes} minutes ago" : 'never before')) do before do subject.update_attributes!( - {state: state.to_s, imported_at: (minutes ? minutes.minutes.ago : nil)}, - {without_protection: true} + {state: state.to_s, + imported_at: (minutes ? minutes.minutes.ago : nil)}, + without_protection: true, ) RepositoryPullingWorker.new.perform end diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb index b05be3495..a630707d1 100644 --- a/spec/models/ontology_version/parsing_spec.rb +++ b/spec/models/ontology_version/parsing_spec.rb @@ -117,7 +117,8 @@ end it 'should use the locid-ref for calling the parse-caller' do - expect { OntologyParsingWorker.drain }.to throw_symbol(:iri, qualified_locid) + expect { OntologyParsingWorker.drain }. + to throw_symbol(:iri, qualified_locid) end end From af571781df36edfb6b32ce66e517b4d5cbdd3286 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 16:14:40 +0100 Subject: [PATCH 195/240] Fix wrong worker class being tested. --- spec/models/ontology_version/parsing_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/models/ontology_version/parsing_spec.rb b/spec/models/ontology_version/parsing_spec.rb index a630707d1..9fb7a85c9 100644 --- a/spec/models/ontology_version/parsing_spec.rb +++ b/spec/models/ontology_version/parsing_spec.rb @@ -227,10 +227,10 @@ before do # Clear Jobs - Worker.jobs.clear + OntologyParsingWorker.jobs.clear stub_hets_for(ontology_path) ontology_version - Worker.drain + OntologyParsingWorker.drain end it '- have sent a request with input-type' do @@ -253,10 +253,10 @@ before do # Clear Jobs - Worker.jobs.clear + OntologyParsingWorker.jobs.clear stub_hets_for(ontology_path) ontology_version - Worker.drain + OntologyParsingWorker.drain end it '- have sent a request with input-type' do From cc33aa3b98466511c8b1a4fdbe30b43708695250 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 12:16:59 +0100 Subject: [PATCH 196/240] Allow to delete an ontology if it is only imported from the same file. --- app/models/ontology.rb | 2 +- app/models/ontology/import_mappings.rb | 10 ++++++++ spec/factories/ontology_factory.rb | 15 +++++++++++ spec/models/ontology_spec.rb | 35 ++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/app/models/ontology.rb b/app/models/ontology.rb index 56b76da23..081a283d7 100644 --- a/app/models/ontology.rb +++ b/app/models/ontology.rb @@ -110,7 +110,7 @@ def can_be_deleted? end def can_be_deleted_alone? - !is_imported? + !is_imported_from_other_file? end def can_be_deleted_with_whole_repository? diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index f11f64fbf..b8d97a2ec 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -11,6 +11,10 @@ def is_imported_from_other_repository? import_mappings_from_other_repositories.present? end + def is_imported_from_other_file? + import_mappings_from_other_files.present? + end + def mapping_targets source_mappings.map(&:target) end @@ -31,6 +35,12 @@ def import_mappings Mapping.where(source_id: id, kind: 'import') end + def import_mappings_from_other_files + import_mappings.reject do |l| + l.target.repository == repository && l.target.path == path + end + end + def import_mappings_from_other_repositories import_mappings.select { |l| l.target.repository != repository } end diff --git a/spec/factories/ontology_factory.rb b/spec/factories/ontology_factory.rb index 64beb088c..56d0c6955 100644 --- a/spec/factories/ontology_factory.rb +++ b/spec/factories/ontology_factory.rb @@ -118,6 +118,21 @@ end end + trait :with_children do + after(:build) do |ontology| + ontology.children << FactoryGirl.build(:ontology, + parent: ontology, + repository: ontology.repository, + basepath: ontology.basepath, + file_extension: ontology.file_extension) + ontology.children << FactoryGirl.build(:ontology, + parent: ontology, + repository: ontology.repository, + basepath: ontology.basepath, + file_extension: ontology.file_extension) + end + end + trait :with_versioned_children do after(:build) do |ontology| version = ontology.versions.build({ diff --git a/spec/models/ontology_spec.rb b/spec/models/ontology_spec.rb index 134c9ced3..ad5d6118c 100644 --- a/spec/models/ontology_spec.rb +++ b/spec/models/ontology_spec.rb @@ -148,17 +148,42 @@ end context 'an imported ontology' do - let(:ontology) { create :ontology } + let(:parent_ontology) { create :distributed_ontology, :with_children } + let(:ontology) { parent_ontology.children.first } + let(:sibling_ontology) { parent_ontology.children.last } before do stub = ->(_u, _t, _m, &block) { block.call('0'*40) } allow_any_instance_of(Repository).to receive(:delete_file, &stub) end - it 'should not be allowed' do - importing = create :ontology - create :import_mapping, target: importing, source: ontology - expect { ontology.destroy_with_parent(user) }.to raise_error(Ontology::DeleteError) + context 'imported by an onology in a different repository' do + let(:importing) { create :ontology } + before { create :import_mapping, target: importing, source: ontology } + + it 'should not be allowed' do + expect { ontology.destroy_with_parent(user) }. + to raise_error(Ontology::DeleteError) + end + end + + context 'imported by an onology in the same repository but another file' do + let(:importing) { create :ontology, repository: ontology.repository } + before { create :import_mapping, target: importing, source: ontology } + + it 'should not be allowed' do + expect { ontology.destroy_with_parent(user) }. + to raise_error(Ontology::DeleteError) + end + end + + context 'imported by an onology in the same file' do + let(:importing) { sibling_ontology } + before { create :import_mapping, target: importing, source: ontology } + + it 'should be allowed' do + expect { ontology.destroy_with_parent(user) }.to_not raise_error + end end end end From 513dddcb34d6e2551fc4cfe5d9fd02afa5b8ce92 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 12:17:53 +0100 Subject: [PATCH 197/240] Disallow ontology deletion if its sibling is imported. --- app/models/ontology.rb | 3 ++- spec/models/ontology_spec.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/models/ontology.rb b/app/models/ontology.rb index 081a283d7..db4e3381c 100644 --- a/app/models/ontology.rb +++ b/app/models/ontology.rb @@ -110,7 +110,8 @@ def can_be_deleted? end def can_be_deleted_alone? - !is_imported_from_other_file? + !is_imported_from_other_file? && + children.all?(&:can_be_deleted_alone?) end def can_be_deleted_with_whole_repository? diff --git a/spec/models/ontology_spec.rb b/spec/models/ontology_spec.rb index ad5d6118c..0551a5004 100644 --- a/spec/models/ontology_spec.rb +++ b/spec/models/ontology_spec.rb @@ -167,6 +167,16 @@ end end + context 'with sibling imported by an onology in a different repository' do + let(:importing) { create :ontology } + before { create :import_mapping, target: importing, source: sibling_ontology } + + it 'should not be allowed' do + expect { ontology.destroy_with_parent(user) }. + to raise_error(Ontology::DeleteError) + end + end + context 'imported by an onology in the same repository but another file' do let(:importing) { create :ontology, repository: ontology.repository } before { create :import_mapping, target: importing, source: ontology } From 91df8dad5f8fb3c5ce3c52e4085738fe02dc92ef Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 12:26:29 +0100 Subject: [PATCH 198/240] Fix ontology deletion modal header. --- app/views/ontologies/_menu.html.haml | 2 +- config/locales/en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/ontologies/_menu.html.haml b/app/views/ontologies/_menu.html.haml index 0f74218bf..811b2fd4c 100644 --- a/app/views/ontologies/_menu.html.haml +++ b/app/views/ontologies/_menu.html.haml @@ -29,4 +29,4 @@ %ul.dropdown-menu{role: 'menu'} - %w(xml json).each do |f| %li= link_to f.upcase, format: f -= modal_body("header_text", t('ontology.delete_confirm.text', name: resource, parent_text: resource.parent.present? ? t('ontology.delete_confirm.parent') : '', children_text: resource.children.present? ? t('ontology.delete_confirm.children') : '', path: resource.path), resource_chain, t('ontology.delete')) += modal_body(t('ontology.delete_confirm.header_text', oms: Settings.OMS), t('ontology.delete_confirm.text', name: resource, parent_text: resource.parent.present? ? t('ontology.delete_confirm.parent') : '', children_text: resource.children.present? ? t('ontology.delete_confirm.children') : '', path: resource.path), resource_chain, t('ontology.delete')) diff --git a/config/locales/en.yml b/config/locales/en.yml index 72c0fc9f2..14caa2cff 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -89,6 +89,7 @@ en: delete: Delete delete_error: Can't delete an %{oms} that's imported by another one. delete_confirm: + header_text: Are you sure about deleting the %{oms}? text: "Really delete the ontology %{name}?\n\nThe %{parent_text}%{children_text}defining file %{path} will be deleted as well." parent: 'parent ontology including its children and the ' children: 'subontologies and the ' From da2e160129e692e4675cb5840d662b62ca39ad18 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 12:28:27 +0100 Subject: [PATCH 199/240] Fix OntolgiesController destroy action. --- app/controllers/ontologies_controller.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 59a8edc81..c2b632035 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -73,13 +73,13 @@ def show end def destroy - if resource.is_imported? - flash[:error] = "Can't delete #{Settings.OMS.with_indefinite_article} - that is imported by another one." - redirect_to resource_chain - else + if resource.can_be_deleted? resource.destroy_with_parent(current_user) destroy! + else + flash[:error] = "Can't delete #{Settings.OMS.with_indefinite_article} + that (contains a child that) is imported by another one." + redirect_to resource_chain end end From 2a9de31c2c0f75f5d3b4eea0204c9da80abaf134 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 12:34:52 +0100 Subject: [PATCH 200/240] Fix i18n on ontology deletion error. --- app/controllers/ontologies_controller.rb | 5 +++-- app/views/ontologies/_menu.html.haml | 2 +- config/locales/en.yml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index c2b632035..5067917a3 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -77,8 +77,9 @@ def destroy resource.destroy_with_parent(current_user) destroy! else - flash[:error] = "Can't delete #{Settings.OMS.with_indefinite_article} - that (contains a child that) is imported by another one." + flash[:error] = t('ontology.delete_error', + oms_with_article: Settings.OMS.with_indefinite_article, + oms: Settings.OMS) redirect_to resource_chain end end diff --git a/app/views/ontologies/_menu.html.haml b/app/views/ontologies/_menu.html.haml index 811b2fd4c..2a11760cf 100644 --- a/app/views/ontologies/_menu.html.haml +++ b/app/views/ontologies/_menu.html.haml @@ -18,7 +18,7 @@ - if resource.can_be_deleted? = modal_button(t('ontology.delete'), btn_class: 'btn-default') - else - .btn.btn-default.tooltip-btn{ disabled: true, data: { toggle: "tooltip", :'original-title' => t('ontology.delete_error', oms: Settings.OMS), container: 'body' } } + .btn.btn-default.tooltip-btn{ disabled: true, data: { toggle: "tooltip", :'original-title' => t('ontology.delete_error', oms_with_article: Settings.OMS.with_indefinite_article, oms: Settings.OMS), container: 'body' } } %span.allow-tooltips Delete - if Settings.format_selection diff --git a/config/locales/en.yml b/config/locales/en.yml index 14caa2cff..0711a15a4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -87,7 +87,7 @@ en: info: The defining file %{path} of this %{oms} was deleted. However, you can check the last_file_version: last version of that file delete: Delete - delete_error: Can't delete an %{oms} that's imported by another one. + delete_error: Can't delete %{oms_with_article} that (contains a child that) is imported by another %{oms}. delete_confirm: header_text: Are you sure about deleting the %{oms}? text: "Really delete the ontology %{name}?\n\nThe %{parent_text}%{children_text}defining file %{path} will be deleted as well." From fe9614d1def0343debe43525464988f151152e92 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 17:13:54 +0100 Subject: [PATCH 201/240] Remove mapping kind 'import' restriction. --- app/models/ontology/import_mappings.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index b8d97a2ec..1e0ef9329 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -1,4 +1,7 @@ class Ontology + # An ontology being imported into is in this context not only a Mapping with + # kind 'import' but any kind of mapping. Otherwise the target ontology's view, + # alignment, etc. is invalid. module ImportMappings extend ActiveSupport::Concern @@ -32,7 +35,7 @@ def direct_imported_ontologies protected def import_mappings - Mapping.where(source_id: id, kind: 'import') + Mapping.where(source_id: id) end def import_mappings_from_other_files From ba1b16ca8166ea93ed96e11e6892a9d5337adaa7 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 17:15:29 +0100 Subject: [PATCH 202/240] Remove unused method. --- app/models/ontology/import_mappings.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index 1e0ef9329..a0d202840 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -6,10 +6,6 @@ module ImportMappings extend ActiveSupport::Concern included do - def is_imported? - import_mappings.present? - end - def is_imported_from_other_repository? import_mappings_from_other_repositories.present? end From dc9ab44a60ba7e645db3b01728e67d91dac0fd97 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 17:19:52 +0100 Subject: [PATCH 203/240] Remove redundant method. --- app/models/ontology/import_mappings.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index a0d202840..22ab58eaf 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -30,18 +30,14 @@ def direct_imported_ontologies protected - def import_mappings - Mapping.where(source_id: id) - end - def import_mappings_from_other_files - import_mappings.reject do |l| + source_mappings.reject do |l| l.target.repository == repository && l.target.path == path end end def import_mappings_from_other_repositories - import_mappings.select { |l| l.target.repository != repository } + source_mappings.select { |l| l.target.repository != repository } end end end From a175267206c317be4f57d42e6f1ffb6e9e6ddbdb Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 17:25:40 +0100 Subject: [PATCH 204/240] Rename methods, remove bloating methods. --- app/models/ontology.rb | 4 ++-- app/models/ontology/import_mappings.rb | 14 ++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/app/models/ontology.rb b/app/models/ontology.rb index db4e3381c..56de3e3e0 100644 --- a/app/models/ontology.rb +++ b/app/models/ontology.rb @@ -110,12 +110,12 @@ def can_be_deleted? end def can_be_deleted_alone? - !is_imported_from_other_file? && + !source_mappings_from_other_files.any? && children.all?(&:can_be_deleted_alone?) end def can_be_deleted_with_whole_repository? - !is_imported_from_other_repository? + !source_mappings_from_other_repositories.any? end def contains_logic_translations? diff --git a/app/models/ontology/import_mappings.rb b/app/models/ontology/import_mappings.rb index 22ab58eaf..53c2ec2fe 100644 --- a/app/models/ontology/import_mappings.rb +++ b/app/models/ontology/import_mappings.rb @@ -6,14 +6,6 @@ module ImportMappings extend ActiveSupport::Concern included do - def is_imported_from_other_repository? - import_mappings_from_other_repositories.present? - end - - def is_imported_from_other_file? - import_mappings_from_other_files.present? - end - def mapping_targets source_mappings.map(&:target) end @@ -28,15 +20,13 @@ def direct_imported_ontologies Ontology.where(id: ontology_ids) end - protected - - def import_mappings_from_other_files + def source_mappings_from_other_files source_mappings.reject do |l| l.target.repository == repository && l.target.path == path end end - def import_mappings_from_other_repositories + def source_mappings_from_other_repositories source_mappings.select { |l| l.target.repository != repository } end end From 45cb61cbe53ce4eef00e2354c8ef5bc550e875de Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Fri, 8 Jan 2016 17:43:11 +0100 Subject: [PATCH 205/240] Fix deletion specs a) Some specs created distributed ontologies where the children have mappings to the other children. This is not intended. These specs only need a distributed ontology with children. b) Specs that care about ontologies that import other ontologies do not need to have import mappings. Any kind of mapping is needed. --- spec/models/ontology_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/models/ontology_spec.rb b/spec/models/ontology_spec.rb index 0551a5004..e321d8805 100644 --- a/spec/models/ontology_spec.rb +++ b/spec/models/ontology_spec.rb @@ -98,7 +98,7 @@ end context 'a single ontology in a distributed ontology' do - let(:distributed_ontology) { create :linked_distributed_ontology } + let(:distributed_ontology) { create :distributed_ontology, :with_children } let(:ontology) { distributed_ontology.children.first } before do @@ -125,7 +125,7 @@ end context 'a distributed ontology' do - let (:ontology) { create :linked_distributed_ontology } + let (:ontology) { create :distributed_ontology, :with_children } before do stub = ->(_u, _t, _m, &block) { block.call('0'*40) } @@ -147,7 +147,7 @@ end end - context 'an imported ontology' do + context 'an imported ontology (meaning any kind of mapping)' do let(:parent_ontology) { create :distributed_ontology, :with_children } let(:ontology) { parent_ontology.children.first } let(:sibling_ontology) { parent_ontology.children.last } @@ -159,7 +159,7 @@ context 'imported by an onology in a different repository' do let(:importing) { create :ontology } - before { create :import_mapping, target: importing, source: ontology } + before { create :mapping, target: importing, source: ontology } it 'should not be allowed' do expect { ontology.destroy_with_parent(user) }. @@ -169,7 +169,7 @@ context 'with sibling imported by an onology in a different repository' do let(:importing) { create :ontology } - before { create :import_mapping, target: importing, source: sibling_ontology } + before { create :mapping, target: importing, source: sibling_ontology } it 'should not be allowed' do expect { ontology.destroy_with_parent(user) }. @@ -179,7 +179,7 @@ context 'imported by an onology in the same repository but another file' do let(:importing) { create :ontology, repository: ontology.repository } - before { create :import_mapping, target: importing, source: ontology } + before { create :mapping, target: importing, source: ontology } it 'should not be allowed' do expect { ontology.destroy_with_parent(user) }. @@ -189,7 +189,7 @@ context 'imported by an onology in the same file' do let(:importing) { sibling_ontology } - before { create :import_mapping, target: importing, source: ontology } + before { create :mapping, target: importing, source: ontology } it 'should be allowed' do expect { ontology.destroy_with_parent(user) }.to_not raise_error From 0b436a39408ef657a96f7e89e0cccd3d3dd86532 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 16:24:31 +0100 Subject: [PATCH 206/240] Style fixes. --- spec/factories/ontology_factory.rb | 22 +++++++++++----------- spec/models/ontology_spec.rb | 6 ++++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/spec/factories/ontology_factory.rb b/spec/factories/ontology_factory.rb index 56d0c6955..b2d5fb806 100644 --- a/spec/factories/ontology_factory.rb +++ b/spec/factories/ontology_factory.rb @@ -119,17 +119,17 @@ end trait :with_children do - after(:build) do |ontology| - ontology.children << FactoryGirl.build(:ontology, - parent: ontology, - repository: ontology.repository, - basepath: ontology.basepath, - file_extension: ontology.file_extension) - ontology.children << FactoryGirl.build(:ontology, - parent: ontology, - repository: ontology.repository, - basepath: ontology.basepath, - file_extension: ontology.file_extension) + after(:build) do |built_ontology| + built_ontology.children << FactoryGirl.build(:ontology, + parent: built_ontology, + repository: built_ontology.repository, + basepath: built_ontology.basepath, + file_extension: built_ontology.file_extension) + built_ontology.children << FactoryGirl.build(:ontology, + parent: built_ontology, + repository: built_ontology.repository, + basepath: built_ontology.basepath, + file_extension: built_ontology.file_extension) end end diff --git a/spec/models/ontology_spec.rb b/spec/models/ontology_spec.rb index e321d8805..ef4954961 100644 --- a/spec/models/ontology_spec.rb +++ b/spec/models/ontology_spec.rb @@ -98,7 +98,9 @@ end context 'a single ontology in a distributed ontology' do - let(:distributed_ontology) { create :distributed_ontology, :with_children } + let(:distributed_ontology) do + create :distributed_ontology, :with_children + end let(:ontology) { distributed_ontology.children.first } before do @@ -125,7 +127,7 @@ end context 'a distributed ontology' do - let (:ontology) { create :distributed_ontology, :with_children } + let(:ontology) { create :distributed_ontology, :with_children } before do stub = ->(_u, _t, _m, &block) { block.call('0'*40) } From 24c99038b338f4b04165e141fdde5ac628b6a77a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 13:50:39 +0100 Subject: [PATCH 207/240] Add missing dependent: :destroy. --- app/models/axiom.rb | 2 +- app/models/axiom_selection.rb | 2 +- app/models/ontology_member/symbol.rb | 5 +++-- app/models/proof_attempt.rb | 4 ++-- app/models/sine_axiom_selection.rb | 2 ++ 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/models/axiom.rb b/app/models/axiom.rb index 10e8e6244..767b3d564 100644 --- a/app/models/axiom.rb +++ b/app/models/axiom.rb @@ -1,3 +1,3 @@ class Axiom < Sentence - has_many :sine_symbol_axiom_triggers + has_many :sine_symbol_axiom_triggers, dependent: :destroy end diff --git a/app/models/axiom_selection.rb b/app/models/axiom_selection.rb index ce79e7a43..69c2a8a61 100644 --- a/app/models/axiom_selection.rb +++ b/app/models/axiom_selection.rb @@ -5,7 +5,7 @@ class AxiomSelection < ActiveRecord::Base attr_accessible :finished - has_many :proof_attempt_configurations + has_many :proof_attempt_configurations, dependent: :destroy has_and_belongs_to_many :axioms, class_name: 'Axiom', association_foreign_key: 'sentence_id', diff --git a/app/models/ontology_member/symbol.rb b/app/models/ontology_member/symbol.rb index 7c93298b5..7c12a1878 100644 --- a/app/models/ontology_member/symbol.rb +++ b/app/models/ontology_member/symbol.rb @@ -12,8 +12,9 @@ class Symbol < ActiveRecord::Base has_and_belongs_to_many :oops_responses # associations for SineAxiomSelection - has_one :sine_symbol_commonness, class_name: SineSymbolCommonness - has_many :sine_symbol_axiom_triggers + has_one :sine_symbol_commonness, class_name: SineSymbolCommonness, + dependent: :destroy + has_many :sine_symbol_axiom_triggers, dependent: :destroy attr_accessible :locid attr_accessible :label, :comment diff --git a/app/models/proof_attempt.rb b/app/models/proof_attempt.rb index f33153321..769b36c3e 100644 --- a/app/models/proof_attempt.rb +++ b/app/models/proof_attempt.rb @@ -7,10 +7,10 @@ class ProofAttempt < ActiveRecord::Base belongs_to :theorem, foreign_key: 'sentence_id' belongs_to :proof_status belongs_to :prover - belongs_to :proof_attempt_configuration + belongs_to :proof_attempt_configuration, dependent: :destroy has_one :axiom_selection, through: :proof_attempt_configuration has_one :prover_output - has_one :tactic_script + has_one :tactic_script, dependent: :destroy has_many :generated_axioms, dependent: :destroy has_and_belongs_to_many :used_axioms, class_name: 'Axiom', diff --git a/app/models/sine_axiom_selection.rb b/app/models/sine_axiom_selection.rb index 3e020f8cb..512f6a441 100644 --- a/app/models/sine_axiom_selection.rb +++ b/app/models/sine_axiom_selection.rb @@ -8,6 +8,8 @@ class SineAxiomSelection < ActiveRecord::Base acts_as :axiom_selection attr_accessible :commonness_threshold, :depth_limit, :tolerance + has_many :sine_symbol_commonnesses, dependent: :destroy + has_many :sine_symbol_axiom_triggers, dependent: :destroy validates_numericality_of :commonness_threshold, greater_than_or_equal_to: 0, From b8537b4ba4c8b80ed53a294e13d9a2b52a6ee14c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 16:22:44 +0100 Subject: [PATCH 208/240] Add label for no_result state. --- lib/state.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/state.rb b/lib/state.rb index 61329814d..a37aa7822 100644 --- a/lib/state.rb +++ b/lib/state.rb @@ -3,12 +3,13 @@ module State TERMINAL_STATES = %w(failed no_result done) STATE_LABEL = { - pending: 'label-warning', - fetching: 'label-primary', - processing: 'label-primary', - done: 'label-success', - failed: 'label-danger', - } + failed: 'label-danger', + no_result: 'label-warning', + pending: 'label-warning', + fetching: 'label-primary', + processing: 'label-primary', + done: 'label-success', + } def self.terminal?(state) TERMINAL_STATES.include?(state) From fd004800ba27d111f70efde0519688e7a0567793 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 16:23:06 +0100 Subject: [PATCH 209/240] Add state not_started_yet This state is to be used whenever a task has not been started yet (and not been scheduled). --- lib/state.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/state.rb b/lib/state.rb index a37aa7822..f506d08ef 100644 --- a/lib/state.rb +++ b/lib/state.rb @@ -1,8 +1,9 @@ module State - STATES = %w(failed no_result pending fetching processing done) + STATES = %w(not_started_yet failed no_result pending fetching processing done) TERMINAL_STATES = %w(failed no_result done) STATE_LABEL = { + not_started_yet: 'label-default', failed: 'label-danger', no_result: 'label-warning', pending: 'label-warning', From a7cfdb0bbc9cc7837770f04e59deee324898049b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 16:27:45 +0100 Subject: [PATCH 210/240] Set state to not_started_yet on new theorem. --- app/models/ontology/sentences.rb | 2 +- spec/factories/theorem_factory.rb | 2 +- spec/models/theorem_spec.rb | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/models/ontology/sentences.rb b/app/models/ontology/sentences.rb index 469b97725..ff406e8b9 100644 --- a/app/models/ontology/sentences.rb +++ b/app/models/ontology/sentences.rb @@ -155,7 +155,7 @@ def set_theorem_attributes(theorem, hash) theorem.state = 'done' else status_id = ProofStatus::DEFAULT_OPEN_STATUS - theorem.state = 'pending' + theorem.state = 'not_started_yet' end theorem.proof_status = ProofStatus.find(status_id) end diff --git a/spec/factories/theorem_factory.rb b/spec/factories/theorem_factory.rb index 459d99e78..be59f0ecd 100644 --- a/spec/factories/theorem_factory.rb +++ b/spec/factories/theorem_factory.rb @@ -3,7 +3,7 @@ name { generate :sentence_name } text { Faker::Lorem.sentence } proof_status { create :proof_status_open } - state { 'pending' } + state { 'not_started_yet' } theorem.after(:build) do |theorem| if !theorem.ontology diff --git a/spec/models/theorem_spec.rb b/spec/models/theorem_spec.rb index 1934377cc..92f67dfff 100644 --- a/spec/models/theorem_spec.rb +++ b/spec/models/theorem_spec.rb @@ -50,12 +50,6 @@ end end end - - context 'state' do - it "is 'pending'" do - expect(theorem.state).to eq('pending') - end - end end context 'update_proof_status!' do From 0b785854e418c7e33243a7e9ef19a248bfb6495b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 16:55:43 +0100 Subject: [PATCH 211/240] Fix states display. --- app/helpers/state_helper.rb | 4 ++-- config/locales/en.yml | 8 ++++++++ lib/state.rb | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/helpers/state_helper.rb b/app/helpers/state_helper.rb index eaf3e450d..a7a899387 100644 --- a/app/helpers/state_helper.rb +++ b/app/helpers/state_helper.rb @@ -44,9 +44,9 @@ def state_tag(resource) end def state(resource) - html = content_tag(:span, resource.state) + html = content_tag(:span, t("states.#{resource.state}")) - unless State::TERMINAL_STATES.include?(resource.state) + unless State::IDLE_STATES.include?(resource.state) html << " " << image_tag('spinner-16x16.gif', class: 'spinner') end diff --git a/config/locales/en.yml b/config/locales/en.yml index 72c0fc9f2..0f8f9a1fb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -12,6 +12,14 @@ en: The threshold level is: %{minimal_count}. wait: 'Please wait …' state: 'Evaluation State' + states: + not_started_yet: not started yet + failed: failed + no_result: no result + pending: pending + fetching: fetching + processing: processing + done: done relation_list: team_user: placeholder: Add users to your team diff --git a/lib/state.rb b/lib/state.rb index f506d08ef..44dc76a90 100644 --- a/lib/state.rb +++ b/lib/state.rb @@ -1,6 +1,7 @@ module State STATES = %w(not_started_yet failed no_result pending fetching processing done) TERMINAL_STATES = %w(failed no_result done) + IDLE_STATES = TERMINAL_STATES + %w(not_started_yet) STATE_LABEL = { not_started_yet: 'label-default', From faacb139b966556c3259a5a7fe0d0ed9e66c3a7a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Tue, 12 Jan 2016 17:18:19 +0100 Subject: [PATCH 212/240] Add bootstrap label to evaluation state widget. --- app/helpers/state_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/state_helper.rb b/app/helpers/state_helper.rb index a7a899387..d2e922b95 100644 --- a/app/helpers/state_helper.rb +++ b/app/helpers/state_helper.rb @@ -30,7 +30,7 @@ def state_tag(resource) resource = resource.is_a?(Ontology) ? resource.current_version : resource html_opts = { - class: "evaluation-state", + class: "evaluation-state label #{State::STATE_LABEL[resource.state.to_sym]}", data: { klass: resource.class.to_s, id: resource.id, From e568cc4d63d7d940acafc114bc74287b9cc0e480 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 13 Jan 2016 09:23:05 +0100 Subject: [PATCH 213/240] Use proper JSON schema files. --- features/step_definitions/combination_steps.rb | 2 +- spec/controllers/api/v1/children_controller_spec.rb | 2 +- spec/controllers/api/v1/mappings_controller_spec.rb | 2 +- .../api/v1/ontology_versions_controller_spec.rb | 2 +- .../controllers/api/v1/sentences_controller_spec.rb | 2 +- spec/controllers/api/v1/symbols_controller_spec.rb | 2 +- spec/support/common_helper_methods.rb | 13 ++++++++++++- 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/features/step_definitions/combination_steps.rb b/features/step_definitions/combination_steps.rb index 4602c7911..1c9969e6a 100644 --- a/features/step_definitions/combination_steps.rb +++ b/features/step_definitions/combination_steps.rb @@ -63,7 +63,7 @@ end Then(/^the body should be valid for a (\d+) combination-response/) do |status| - schema = schema_for("repository/combinations/POST/response/#{status}") + schema = schema_for("generic/#{status}") VCR.use_cassette "api/json-schemata/repository/combinations/#{status}" do expect(last_response.body).to match_json_schema(schema) end diff --git a/spec/controllers/api/v1/children_controller_spec.rb b/spec/controllers/api/v1/children_controller_spec.rb index a3c6b7cfd..dfc8d03f1 100644 --- a/spec/controllers/api/v1/children_controller_spec.rb +++ b/spec/controllers/api/v1/children_controller_spec.rb @@ -7,7 +7,7 @@ context 'on GET to index' do context 'requesting json representation', api_specification: true do let(:children_schema) do - schema_for('ontology/commands/children') + schema_for_command('ontology/children', :get, 200) end before do diff --git a/spec/controllers/api/v1/mappings_controller_spec.rb b/spec/controllers/api/v1/mappings_controller_spec.rb index 143f38996..9dc5ab50f 100644 --- a/spec/controllers/api/v1/mappings_controller_spec.rb +++ b/spec/controllers/api/v1/mappings_controller_spec.rb @@ -8,7 +8,7 @@ context 'on GET to index' do context 'requesting json representation', api_specification: true do let(:mappings_schema) do - schema_for('ontology/commands/mappings') + schema_for_command('ontology/mappings', :get, 200) end before do diff --git a/spec/controllers/api/v1/ontology_versions_controller_spec.rb b/spec/controllers/api/v1/ontology_versions_controller_spec.rb index 1d8ffada0..a217330da 100644 --- a/spec/controllers/api/v1/ontology_versions_controller_spec.rb +++ b/spec/controllers/api/v1/ontology_versions_controller_spec.rb @@ -8,7 +8,7 @@ context 'on GET to index' do context 'requesting json representation', api_specification: true do let(:ontology_versions_schema) do - schema_for('ontology/commands/ontology_versions') + schema_for_command('ontology/ontology_versions', :get, 200) end before do diff --git a/spec/controllers/api/v1/sentences_controller_spec.rb b/spec/controllers/api/v1/sentences_controller_spec.rb index f37fddc0c..3b2ff993b 100644 --- a/spec/controllers/api/v1/sentences_controller_spec.rb +++ b/spec/controllers/api/v1/sentences_controller_spec.rb @@ -8,7 +8,7 @@ context 'on GET to index' do context 'requesting json representation', api_specification: true do let(:sentences_schema) do - schema_for('ontology/commands/sentences') + schema_for_command('ontology/sentences', :get, 200) end before do diff --git a/spec/controllers/api/v1/symbols_controller_spec.rb b/spec/controllers/api/v1/symbols_controller_spec.rb index ac4cbea84..ba3899595 100644 --- a/spec/controllers/api/v1/symbols_controller_spec.rb +++ b/spec/controllers/api/v1/symbols_controller_spec.rb @@ -8,7 +8,7 @@ context 'on GET to index' do context 'requesting json representation', api_specification: true do let(:symbols_schema) do - schema_for('ontology/commands/symbols') + schema_for_command('ontology/symbols', :get, 200) end before do diff --git a/spec/support/common_helper_methods.rb b/spec/support/common_helper_methods.rb index 9da4900ca..527c72413 100644 --- a/spec/support/common_helper_methods.rb +++ b/spec/support/common_helper_methods.rb @@ -1,5 +1,7 @@ require Rails.root.join('spec', 'support', 'json_schema_matcher.rb') +SCHEMA_BASE_URL = "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/" + def controllers_locid_for(resource, *args, &block) request.env['action_controller.instance']. send(:locid_for, resource, *args, &block) @@ -68,7 +70,16 @@ def stub_hets_instance_url_for_pipeline_generator end def schema_for(name) - "https://masterthesis.rightsrestricted.com/ontohub/#{name}.json" + # We use the develop branch to allow unmerged pull requests to be considered. + "#{SCHEMA_BASE_URL}#{name}.json" +end + +def schema_for_command(command, method = :get, response_code = nil) + if response_code + "#{SCHEMA_BASE_URL}#{command}/#{method.to_s.upcase}/#{response_code}.json" + else + "#{SCHEMA_BASE_URL}#{command}/#{method.to_s.upcase}/request.json" + end end # includes the convenience-method `define_ontology('name')` From 6712dc6136cec06e2277530af5c2cb6ee5b3cdef Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 13 Jan 2016 11:26:36 +0100 Subject: [PATCH 214/240] Put VCR config into shared_helper Cucumber did not save VCR cassettes in the fixtures/vcr directory because VCR was not configured. --- features/support/env.rb | 1 + spec/spec_helper.rb | 18 +----------------- spec/support/shared_helper.rb | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 spec/support/shared_helper.rb diff --git a/features/support/env.rb b/features/support/env.rb index a372dc645..b4552ef5e 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -17,6 +17,7 @@ class Cucumber::Rails::World require Rails.root.join('spec', 'support', 'common_helper_methods.rb') require Rails.root.join('spec', 'support', 'scenario_progress_formatter.rb') + require Rails.root.join('spec', 'support', 'shared_helper.rb') def locid_for(resource, *commands, **query_components) iri = "#{resource.locid}" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f08f53369..bb7963e59 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,8 +13,6 @@ require 'webmock/rspec' WebMock.disable_net_connect!(allow_localhost: true) -elasticsearch_port = ENV['ELASTIC_TEST_PORT'].present? ? ENV['ELASTIC_TEST_PORT'] : '9250' -Elasticsearch::Model.client = Elasticsearch::Client.new host: "localhost:#{elasticsearch_port}" # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -32,6 +30,7 @@ def query_string require Rails.root.join('spec', 'support', 'common_helper_methods.rb') require Rails.root.join('spec', 'support', 'documentation_progress_formatter.rb') +require Rails.root.join('spec', 'support', 'shared_helper.rb') def stub_cp_keys allow(AuthorizedKeysManager).to receive(:copy_authorized_keys_to_git_home) @@ -60,21 +59,6 @@ def stub_hets_instance_force_free_worker allow(HetsInstanceForceFreeWorker).to receive(:perform_in) end -# Recording HTTP Requests -VCR.configure do |c| - c.cassette_library_dir = 'spec/fixtures/vcr' - c.hook_into :webmock - c.ignore_localhost = true - c.ignore_request do |request| - # ignore elasticsearch requests - URI(request.uri).host == 'localhost' && - URI(request.uri).port == elasticsearch_port.to_i - end - c.register_request_matcher :hets_prove_uri do |request1, request2| - hets_prove_matcher(request1, request2) - end -end - RSpec.configure do |config| config.add_setting :current_full_description config.add_setting :current_description diff --git a/spec/support/shared_helper.rb b/spec/support/shared_helper.rb new file mode 100644 index 000000000..c12f3537a --- /dev/null +++ b/spec/support/shared_helper.rb @@ -0,0 +1,17 @@ +elasticsearch_port = ENV['ELASTIC_TEST_PORT'].present? ? ENV['ELASTIC_TEST_PORT'] : '9250' +Elasticsearch::Model.client = Elasticsearch::Client.new host: "localhost:#{elasticsearch_port}" + +# Recording HTTP Requests +VCR.configure do |c| + c.cassette_library_dir = 'spec/fixtures/vcr' + c.hook_into :webmock + c.ignore_localhost = true + c.ignore_request do |request| + # ignore elasticsearch requests + URI(request.uri).host == 'localhost' && + URI(request.uri).port == elasticsearch_port.to_i + end + c.register_request_matcher :hets_prove_uri do |request1, request2| + hets_prove_matcher(request1, request2) + end +end From b2f4e4144156d6028e0ee306551d86d346c59753 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 13 Jan 2016 14:13:27 +0100 Subject: [PATCH 215/240] Remove strict option from JSON schema validator This allows to have keys in the response that are not specified in the schema, which we do not want, but it also actually uses the "required" key of the schema telling which keys are required in the response and which are optional (this is what we definitely need). --- spec/support/json_schema_matcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/json_schema_matcher.rb b/spec/support/json_schema_matcher.rb index 94f5ad210..c46a81f96 100644 --- a/spec/support/json_schema_matcher.rb +++ b/spec/support/json_schema_matcher.rb @@ -3,6 +3,6 @@ RSpec::Matchers.define :match_json_schema do |schema| match do |text| JSON::Validator.clear_cache - JSON::Validator.validate!(schema, text, strict: true, clear_cache: true) + JSON::Validator.validate!(schema, text, clear_cache: true) end end From 405250d46de1f5f91be87e27e8c9438ca23fee8e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 13 Jan 2016 14:53:28 +0100 Subject: [PATCH 216/240] Fix combinations controller not being API-compliant. --- app/controllers/api/v1/combinations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/combinations_controller.rb b/app/controllers/api/v1/combinations_controller.rb index d043145cb..ac71f61ad 100644 --- a/app/controllers/api/v1/combinations_controller.rb +++ b/app/controllers/api/v1/combinations_controller.rb @@ -15,7 +15,7 @@ def create else response.status = 400 render json: {status: response.message, - error: errors_str} + errors: [errors_str]} end end From 4d437fdae7216cb88d5f093833b1c0166ef2a53b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Wed, 13 Jan 2016 10:07:46 +0100 Subject: [PATCH 217/240] Adjust API JSON schema VCR fixtures to use new Github repository. --- .../fixtures/vcr/api/json-schemata/action.yml | 226 +++++++ .../vcr/api/json-schemata/formality_level.yml | 208 +++++- .../vcr/api/json-schemata/license_model.yml | 240 ++++++- spec/fixtures/vcr/api/json-schemata/logic.yml | 208 +++++- .../vcr/api/json-schemata/logic_mapping.yml | 499 ++++++++++----- .../vcr/api/json-schemata/mapping.yml | 488 ++++++++++----- .../vcr/api/json-schemata/ontology.yml | 590 ++++++++++++------ .../api/json-schemata/ontology/children.yml | 385 ++++++++---- .../api/json-schemata/ontology/mappings.yml | 385 ++++++++---- .../ontology/ontology_versions.yml | 385 ++++++++---- .../json-schemata/ontology/proof_attempt.yml | 584 +++++++++++------ .../ontology/proof_attempt_configuration.yml | 467 +++++++++----- .../json-schemata/ontology/prover_output.yml | 435 +++++++++---- .../api/json-schemata/ontology/sentences.yml | 385 ++++++++---- .../api/json-schemata/ontology/symbols.yml | 385 ++++++++---- .../api/json-schemata/ontology/theorem.yml | 457 +++++++++----- .../vcr/api/json-schemata/ontology_type.yml | 249 ++++++-- .../api/json-schemata/ontology_version.yml | 471 +++++++++----- .../vcr/api/json-schemata/proof_status.yml | 207 +++++- .../vcr/api/json-schemata/repository.yml | 205 +++++- .../repository/combinations/202.yml | 226 +++++++ .../repository/combinations/400.yml | 223 +++++++ .../vcr/api/json-schemata/sentence.yml | 440 ++++++++----- .../fixtures/vcr/api/json-schemata/symbol.yml | 483 +++++++++----- 24 files changed, 6470 insertions(+), 2361 deletions(-) create mode 100644 spec/fixtures/vcr/api/json-schemata/action.yml create mode 100644 spec/fixtures/vcr/api/json-schemata/repository/combinations/202.yml create mode 100644 spec/fixtures/vcr/api/json-schemata/repository/combinations/400.yml diff --git a/spec/fixtures/vcr/api/json-schemata/action.yml b/spec/fixtures/vcr/api/json-schemata/action.yml new file mode 100644 index 000000000..1ce7bbb8e --- /dev/null +++ b/spec/fixtures/vcr/api/json-schemata/action.yml @@ -0,0 +1,226 @@ +--- +http_interactions: +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/action.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"c94bf846f0a12c094495f2d8089e1071529bfc8d"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B1D:2C5E:390217:56964F7C + Content-Length: + - '345' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:05 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4143-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 959d8a2edf4d576acbdfbfe8cbafff3943e84c96 + Expires: + - Wed, 13 Jan 2016 13:27:05 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/action.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Action", + "description": "Represents an asynchronous long-running request", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "status": { + "description": "The status of the resource in question", + "type": "string" + }, + "eta": { + "description": "estimated time of arrival in seconds", + "type": "integer" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "status", "eta" + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:05 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B23:2C5C:4019BD:56964F7C + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:05 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4122-AMS + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 226fa093334975f83a234326687a7de3ed51df70 + Expires: + - Wed, 13 Jan 2016 13:27:05 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:05 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/formality_level.yml b/spec/fixtures/vcr/api/json-schemata/formality_level.yml index 769124d58..46bc1b2d3 100644 --- a/spec/fixtures/vcr/api/json-schemata/formality_level.yml +++ b/spec/fixtures/vcr/api/json-schemata/formality_level.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/formality_level.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/formality_level.json body: encoding: US-ASCII string: '' @@ -18,41 +18,61 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:01 GMT - Content-Type: - - application/json - Content-Length: - - '608' - Last-Modified: - - Thu, 26 Mar 2015 10:31:21 GMT - Connection: - - keep-alive - Etag: - - '"5513dff9-260"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"f037a50c6cf644254cb8ea1f1e6962e064d041a0"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1216:377A:2ECD25:56964DC0 + Content-Length: + - '311' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:14:41 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1132-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 891752b29b438c791be4b9eb46e6207681d27960 + Expires: + - Wed, 13 Jan 2016 13:19:41 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/formality_level.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/formality_level.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Formality Level", "description": "A formality level", "type": "object", "properties": { - "iri": { - "description": "The unique identifier for a formality level", - "type": "string", - "format": "uri" - }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, "name": { "description": "The name of the formality level", "type": "string" @@ -60,9 +80,147 @@ http_interactions: "description": { "description": "A description of the formality level", "type": ["string", "null"] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "name" + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:14:41 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:14:42 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1129-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 175f521d4e3e0863e7c6d786478e6bf2d673a642 + Expires: + - Wed, 13 Jan 2016 13:19:42 GMT + Source-Age: + - '128' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:01 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:14:42 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/license_model.yml b/spec/fixtures/vcr/api/json-schemata/license_model.yml index 13fc76c7e..ea9a599e9 100644 --- a/spec/fixtures/vcr/api/json-schemata/license_model.yml +++ b/spec/fixtures/vcr/api/json-schemata/license_model.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/license_model.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/license_model.json body: encoding: US-ASCII string: '' @@ -18,56 +18,230 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:36:23 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"59cb596153c719d2cf33a2d9c28c4f848947e56b"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1223:0F84:2D60DF:56964D46 Content-Length: - - '762' - Last-Modified: - - Thu, 26 Mar 2015 10:31:29 GMT + - '393' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:12:39 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"5513e001-2fa"' + X-Served-By: + - cache-lcy1126-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 4ebd373eb1eb76e3e124b8023a1f551eb92a3bbb + Expires: + - Wed, 13 Jan 2016 13:17:39 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/license_model.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "License Model", + "description": "A license model", + "type": "object", + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the license model", + "type": "string" + }, + "description": { + "description": "A description of the license model", + "type": "string" + }, + "url": { + "description": "A URL which should contain documentation of the license model. Is not part of Ontohub", + "type": "string", + "format": "uri" + } + }, + "required": [ "iri", "name" ] + }, + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the license model", + "type": "string" + }, + "description": { + "description": "A description of the license model", + "type": "string" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ "iri", "name", "_links" ] + } + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:12:39 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:39 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1132-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 27e723168e6e0bcc48f812715214f6ba2e6bc028 + Expires: + - Wed, 13 Jan 2016 13:17:39 GMT + Source-Age: + - '5' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/license_model.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", "$schema": "http://json-schema.org/draft-04/schema#", - "title": "License Model", - "description": "A license model", - "type": "object", - "properties": { + "definitions": { "iri": { - "description": "The unique identifier for a license model", + "description": "The IRI of a resource", "type": "string", - "format": "uri" - }, - "name": { - "description": "The name of the license model", - "type": "string" + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" }, - "description": { - "description": "A description of the license model", - "type": "string" + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } }, - "url": { - "description": "A URL which should contain documentation of the license model. Is not part of Ontohub", - "type": "string", - "format": "uri" + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:36:23 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:12:39 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/logic.yml b/spec/fixtures/vcr/api/json-schemata/logic.yml index b0861b513..332484537 100644 --- a/spec/fixtures/vcr/api/json-schemata/logic.yml +++ b/spec/fixtures/vcr/api/json-schemata/logic.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/logic.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/logic.json body: encoding: US-ASCII string: '' @@ -18,41 +18,61 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:35:27 GMT - Content-Type: - - application/json - Content-Length: - - '812' - Last-Modified: - - Thu, 26 Mar 2015 10:31:44 GMT - Connection: - - keep-alive - Etag: - - '"5513e010-32c"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"5eefb28d826edd2f4b42752dce865ddea906d07d"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1214:377D:418CCE:56964E8E + Content-Length: + - '361' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:07 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1135-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 779a8eae3777d099ad3f2c33db6685970c1e08ae + Expires: + - Wed, 13 Jan 2016 13:23:07 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/logic.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/logic.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Logic", "description": "A logic as registered with Ontohub", "type": "object", "properties": { - "iri": { - "description": "The unique identifier for the logic", - "type": "string", - "format": "uri" - }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, "name": { "description": "The name of the logic", "type": "string" @@ -68,9 +88,147 @@ http_interactions: "defined_by": { "description": "The resource which defined the logic", "type": ["string", "null"] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "name" + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:18:07 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:18:07 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 19bf6e26a83933d5b0e9f7bf93d80596a97ef658 + Expires: + - Wed, 13 Jan 2016 13:23:07 GMT + Source-Age: + - '17' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:35:27 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:18:07 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/logic_mapping.yml b/spec/fixtures/vcr/api/json-schemata/logic_mapping.yml index 57017e157..bc912e25d 100644 --- a/spec/fixtures/vcr/api/json-schemata/logic_mapping.yml +++ b/spec/fixtures/vcr/api/json-schemata/logic_mapping.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/logic_mapping.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/logic_mapping.json body: encoding: US-ASCII string: '' @@ -18,85 +18,149 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:52 GMT - Content-Type: - - application/json - Content-Length: - - '1441' - Last-Modified: - - Sat, 04 Apr 2015 16:37:22 GMT - Connection: - - keep-alive - Etag: - - '"55201342-5a1"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"0e4452dbee4386407b06099a49c38eace5f7f328"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1220:377B:36DB44:56964E83 + Content-Length: + - '512' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:57 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1131-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 0fee53e8f37ba610f42ab33e0af2ebe6b25f8388 + Expires: + - Wed, 13 Jan 2016 13:22:57 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/logic_mapping.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/logic_mapping.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Logic Mapping", "description": "A mapping between two logics", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the logic-mapping", - "type": "string", - "format": "uri" - }, - "standardization_status": { - "description": "The standardization status of the mapping", - "type": "string" - }, - "defined_by": { - "description": "The authority that defined the mapping", - "type": ["string", "null"] - }, - "faithfulness": { - "description": "Degree of faithfulness of the mapping", - "type": "string" - }, - "exactness": { - "description": "Degree of exactness of the mapping", - "type": "string" - }, - "theoroidalness": { - "description": "Degree of theoroidalness of the mapping", - "type": "string" - }, - "projection": { - "description": "Describes whether the mapping is a projection", - "type": "boolean" + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "standardization_status": { + "description": "The standardization status of the mapping", + "type": "string" + }, + "defined_by": { + "description": "The authority that defined the mapping", + "type": ["string", "null"] + }, + "faithfulness": { + "description": "Degree of faithfulness of the mapping", + "type": "string" + }, + "exactness": { + "description": "Degree of exactness of the mapping", + "type": "string" + }, + "theoroidalness": { + "description": "Degree of theoroidalness of the mapping", + "type": "string" + }, + "projection": { + "description": "Describes whether the mapping is a projection", + "type": "boolean" + }, + "source_logic": { "$ref": "references.json#/definitions/logic" }, + "target_logic": { "$ref": "references.json#/definitions/logic" } + }, + "required": [ + "iri", + "standardization_status", + "defined_by", + "faithfulness", + "theoroidalness", + "exactness", + "projection", + "source_logic", + "target_logic" + ] }, - "source_logic": { "$ref": "references.json#/definitions/logic" }, - "target_logic": { "$ref": "references.json#/definitions/logic" } - }, - "required": [ - "iri", - "standardization_status", - "defined_by", - "faithfulness", - "theoroidalness", - "exactness", - "projection", - "source_logic", - "target_logic" + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "standardization_status": { + "description": "The standardization status of the mapping", + "type": "string" + }, + "defined_by": { + "description": "The authority that defined the mapping", + "type": ["string", "null"] + }, + "faithfulness": { + "description": "Degree of faithfulness of the mapping", + "type": "string" + }, + "exactness": { + "description": "Degree of exactness of the mapping", + "type": "string" + }, + "theoroidalness": { + "description": "Degree of theoroidalness of the mapping", + "type": "string" + }, + "projection": { + "description": "Describes whether the mapping is a projection", + "type": "boolean" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "standardization_status", + "defined_by", + "faithfulness", + "theoroidalness", + "exactness", + "projection", + "_links" + ] + } ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:52 GMT + recorded_at: Wed, 13 Jan 2016 13:17:57 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -112,31 +176,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:52 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:17:57 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1129-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 2454e972a096cdd1e572be0a5d9c9fb090796836 + Expires: + - Wed, 13 Jan 2016 13:22:57 GMT + Source-Age: + - '7' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:17:57 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:57 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1124-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - ca80002f64d1d890a138bde845846f94aab9d211 + Expires: + - Wed, 13 Jan 2016 13:22:57 GMT + Source-Age: + - '7' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -147,11 +369,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -162,11 +380,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -177,11 +391,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -192,11 +402,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -207,11 +413,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -222,11 +424,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -237,22 +435,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -263,11 +453,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -278,11 +464,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -293,11 +475,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -308,37 +486,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -346,33 +511,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:52 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:17:57 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/mapping.yml b/spec/fixtures/vcr/api/json-schemata/mapping.yml index c2cf422d9..39a533a8b 100644 --- a/spec/fixtures/vcr/api/json-schemata/mapping.yml +++ b/spec/fixtures/vcr/api/json-schemata/mapping.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/mapping.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/mapping.json body: encoding: US-ASCII string: '' @@ -18,82 +18,141 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:47 GMT - Content-Type: - - application/json - Content-Length: - - '1276' - Last-Modified: - - Tue, 31 Mar 2015 17:35:51 GMT - Connection: - - keep-alive - Etag: - - '"551adaf7-4fc"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"3139efce778ada8a61076581a8823da7a6e3f87d"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121D:3779:27C808:56964E8C + Content-Length: + - '455' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:05 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1128-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - e15bb452591bedcedfc31f82f85920b4a0f1a58f + Expires: + - Wed, 13 Jan 2016 13:23:05 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/mapping.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/mapping.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Mapping", "description": "A mapping defined by an ontology", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the mapping", - "type": "string", - "format": "uri" - }, - "name": { - "description": "The name of the mapping", - "type": "string" - }, - "kind": { - "description": "The kind of mapping", - "type": "string" - }, - "theorem": { - "description": "Denotes whether this mapping created a theorem", - "type": "boolean" - }, - "proven": { - "description": "Denotes whether this mapping is proven", - "type": "boolean" - }, - "local": { - "description": "Denotes whether this mapping is local", - "type": "boolean" + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the mapping", + "type": "string" + }, + "kind": { + "description": "The kind of mapping", + "type": "string" + }, + "theorem": { + "description": "Denotes whether this mapping created a theorem", + "type": "boolean" + }, + "proven": { + "description": "Denotes whether this mapping is proven", + "type": "boolean" + }, + "local": { + "description": "Denotes whether this mapping is local", + "type": "boolean" + }, + "ontology": { "$ref": "references.json#/definitions/ontology" }, + "source_ontology": { "$ref": "references.json#/definitions/ontology" }, + "target_ontology": { "$ref": "references.json#/definitions/ontology" } + }, + "required": [ + "iri", + "name", + "kind", + "theorem", + "proven", + "local", + "ontology", + "source_ontology", + "target_ontology" + ] }, - "ontology": { "$ref": "references.json#/definitions/ontology" }, - "source_ontology": { "$ref": "references.json#/definitions/ontology" }, - "target_ontology": { "$ref": "references.json#/definitions/ontology" } - }, - "required": [ - "iri", - "name", - "kind", - "theorem", - "proven", - "local", - "ontology", - "source_ontology", - "target_ontology" + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the mapping", + "type": "string" + }, + "kind": { + "description": "The kind of mapping", + "type": "string" + }, + "theorem": { + "description": "Denotes whether this mapping created a theorem", + "type": "boolean" + }, + "proven": { + "description": "Denotes whether this mapping is proven", + "type": "boolean" + }, + "local": { + "description": "Denotes whether this mapping is local", + "type": "boolean" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "name", + "kind", + "theorem", + "proven", + "local", + "_links" + ] + } ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:47 GMT + recorded_at: Wed, 13 Jan 2016 13:18:05 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -109,31 +168,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:47 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:18:05 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1127-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '4' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - cb49d275a129ab2abc43188eaca87f547c20b0a1 + Expires: + - Wed, 13 Jan 2016 13:23:05 GMT + Source-Age: + - '14' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:18:05 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:05 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1125-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 6a01a33bbf054d4eba11ea27ededda722c234372 + Expires: + - Wed, 13 Jan 2016 13:23:05 GMT + Source-Age: + - '14' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -144,11 +361,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -159,11 +372,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -174,11 +383,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -189,11 +394,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -204,11 +405,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -219,11 +416,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -234,22 +427,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -260,11 +445,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -275,11 +456,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -290,11 +467,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -305,37 +478,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -343,33 +503,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:47 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:18:05 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology.yml b/spec/fixtures/vcr/api/json-schemata/ontology.yml index 4bf408fca..b44d092fe 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology.json body: encoding: US-ASCII string: '' @@ -18,133 +18,198 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:34:19 GMT - Content-Type: - - application/json - Content-Length: - - '2826' - Last-Modified: - - Fri, 27 Mar 2015 09:17:35 GMT - Connection: - - keep-alive - Etag: - - '"5515202f-b0a"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"d81e67e64b0b2aa6d2842cdbce9ee949e0e9f034"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B1C:2C5E:36A505:56964E24 + Content-Length: + - '756' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:20 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4147-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 4c36c699ed1bdd243eb33ec0a76f5e4f175eb9ac + Expires: + - Wed, 13 Jan 2016 13:21:20 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Ontology", "description": "An ontology as recorded by Ontohub", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for an ontology", - "type": "string", - "format": "uri" - }, - "name": { - "description": "The name of the ontology", - "type": "string" - }, - "acronym": { - "description": "Acronym of the name of the ontology", - "type": ["string", "null"] - }, - "description": { - "description": "A short description of the ontology", - "type": "string" - }, - "documentation": { - "description": "Includes a reference (usually a URI) to a documentation resource", - "type": ["string", "null"] - }, - "evaluation_state": { - "description": "The current state of the evaluation of the ontology", - "type": "string" - }, - "basepath": { - "description": "The path to the ontology relative to the git repositories root directory", - "type": "string" - }, - "file_extension": { - "description": "Actual file extension of the ontology file in the repository", - "type": "string" - }, - "logic": { "$ref": "references.json#/definitions/logic" }, - "repository": { "$ref": "references.json#/definitions/repository" }, - "parent": { - "oneOf": [ - { "$ref": "references.json#/definitions/ontology" }, - { "type": "null" } - ] - }, - "ontology_type": { - "oneOf": [ - { "$ref": "references.json#/definitions/ontology_type" }, - { "type": "null" } + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the ontology", + "type": "string" + }, + "acronym": { + "description": "Acronym of the name of the ontology", + "type": ["string", "null"] + }, + "description": { + "description": "A short description of the ontology", + "type": "string" + }, + "documentation": { + "description": "Includes a reference (usually a URI) to a documentation resource", + "type": ["string", "null"] + }, + "evaluation_state": { + "description": "The current state of the evaluation of the ontology", + "type": "string" + }, + "basepath": { + "description": "The path to the ontology relative to the git repositories root directory", + "type": "string" + }, + "file_extension": { + "description": "Actual file extension of the ontology file in the repository", + "type": "string" + }, + "logic": { "$ref": "references.json#/definitions/logic" }, + "repository": { "$ref": "references.json#/definitions/repository" }, + "parent": { + "oneOf": [ + { "$ref": "references.json#/definitions/ontology" }, + { "type": "null" } + ] + }, + "ontology_type": { + "oneOf": [ + { "$ref": "references.json#/definitions/ontology_type" }, + { "type": "null" } + ] + }, + "current_ontology_version": { "$ref": "references.json#/definitions/ontology_version" }, + "license_models": { + "type": "array", + "items": { "$ref": "references.json#/definitions/license_model" } + }, + "formality_level": { + "oneOf": [ + { "$ref": "references.json#/definitions/formality_level" }, + { "type": "null" } + ] + }, + "ontology_versions": { + "description": "Reference to all ontology versions of this ontology", + "type": "string", + "format": "uri" + }, + "symbols": { + "description": "Reference to all symbols of this ontology", + "type": "string", + "format": "uri" + }, + "sentences": { + "description": "Reference to all sentences of this ontology", + "type": "string", + "format": "uri" + }, + "mappings": { + "description": "Reference to all mappings defined by this ontology", + "type": "string", + "format": "uri" + } + }, + "required": [ + "iri", + "logic", + "repository", + "ontology_versions", + "symbols", + "sentences", + "mappings", + "name", + "basepath", + "file_extension" ] }, - "current_ontology_version": { "$ref": "references.json#/definitions/ontology_version" }, - "license_models": { - "type": "array", - "items": { "$ref": "references.json#/definitions/license_model" } - }, - "formality_level": { - "oneOf": [ - { "$ref": "references.json#/definitions/formality_level" }, - { "type": "null" } + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the ontology", + "type": "string" + }, + "acronym": { + "description": "Acronym of the name of the ontology", + "type": ["string", "null"] + }, + "description": { + "description": "A short description of the ontology", + "type": "string" + }, + "documentation": { + "description": "Includes a reference (usually a URI) to a documentation resource", + "type": ["string", "null"] + }, + "evaluation_state": { + "description": "The current state of the evaluation of the ontology", + "type": "string" + }, + "basepath": { + "description": "The path to the ontology relative to the git repositories root directory", + "type": "string" + }, + "file_extension": { + "description": "Actual file extension of the ontology file in the repository", + "type": "string" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "name", + "basepath", + "file_extension", + "_links" ] - }, - "ontology_versions": { - "description": "Reference to all ontology versions of this ontology", - "type": "string", - "format": "uri" - }, - "symbols": { - "description": "Reference to all symbols of this ontology", - "type": "string", - "format": "uri" - }, - "sentences": { - "description": "Reference to all sentences of this ontology", - "type": "string", - "format": "uri" - }, - "mappings": { - "description": "Reference to all mappings defined by this ontology", - "type": "string", - "format": "uri" } - }, - "required": [ - "iri", - "logic", - "repository", - "ontology_versions", - "symbols", - "sentences", - "mappings", - "name", - "basepath", - "file_extension" ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:34:19 GMT + recorded_at: Wed, 13 Jan 2016 13:16:20 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -160,31 +225,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:34:19 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B23:2C5B:3A6A7C:56964E23 Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:16:20 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-ams4125-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - e5af5d7e5381732beb3ee34b75ea79182c456fbd + Expires: + - Wed, 13 Jan 2016 13:21:20 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:16:20 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B25:2C57:252DA3:56964E24 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:20 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4145-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - fc093ee9e3d5ac17742c94c33c2c581647c59c57 + Expires: + - Wed, 13 Jan 2016 13:21:20 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -195,11 +418,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -210,11 +429,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -225,11 +440,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -240,11 +451,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -255,11 +462,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -270,11 +473,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -285,22 +484,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -311,11 +502,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -326,11 +513,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -341,11 +524,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -356,37 +535,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -394,33 +560,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:34:19 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:16:20 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/children.yml b/spec/fixtures/vcr/api/json-schemata/ontology/children.yml index 82c837456..5e8570496 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/children.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/children.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/children.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/children/GET/200.json body: encoding: US-ASCII string: '' @@ -18,42 +18,66 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:33 GMT - Content-Type: - - application/json - Content-Length: - - '386' - Last-Modified: - - Mon, 09 Mar 2015 11:34:00 GMT - Connection: - - keep-alive - Etag: - - '"54fd8528-182"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"825caffff540db9b0cde71cc9bf6bd4189fe29d3"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F131C:377F:41C490:56964D86 + Content-Length: + - '235' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:13:43 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lhr6321-LHR + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 9c0b11d90c02c180b525bebe00d3b9f388945dcd + Expires: + - Wed, 13 Jan 2016 13:18:43 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/children.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/children/GET/200.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "///children", "description": "A list of child-ontology references for that ontology", "type": "array", - "items": { "$ref": "https://masterthesis.rightsrestricted.com/ontohub/references.json#/definitions/ontology" } + "items": { "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#/definitions/ontology" } } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:33 GMT + recorded_at: Wed, 13 Jan 2016 13:13:43 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -69,31 +93,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:34 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F131C:3778:1D04E2:56964D87 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:13:43 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lhr6323-LHR + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - edfe37dcd5bf8879b624abcf6980ffb23adc4605 + Expires: + - Wed, 13 Jan 2016 13:18:43 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -104,11 +152,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -119,11 +163,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -134,11 +174,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -149,11 +185,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -164,11 +196,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -179,11 +207,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -194,22 +218,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -220,11 +236,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -235,11 +247,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -250,11 +258,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -265,37 +269,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -303,33 +294,185 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:34 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:13:43 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F131B:377F:41C5E7:56964D87 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:13:43 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lhr6334-LHR + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 8d29ffd93e9587ee6530f241c444812cf96f96e9 + Expires: + - Wed, 13 Jan 2016 13:18:43 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:13:43 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/mappings.yml b/spec/fixtures/vcr/api/json-schemata/ontology/mappings.yml index f661db0f0..6a82b41d7 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/mappings.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/mappings.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/mappings.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/mappings/GET/200.json body: encoding: US-ASCII string: '' @@ -18,42 +18,66 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:46 GMT - Content-Type: - - application/json - Content-Length: - - '378' - Last-Modified: - - Sun, 08 Mar 2015 20:42:04 GMT - Connection: - - keep-alive - Etag: - - '"54fcb41c-17a"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"0f2ea3f81845822178567f4ae45228e6307b76ee"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:377B:36EFDE:56964E8E + Content-Length: + - '234' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:06 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1131-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 42967284ce12e7a4f1228a0ba70bea5b93d3d408 + Expires: + - Wed, 13 Jan 2016 13:23:06 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/mappings.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/mappings/GET/200.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "///mappings", "description": "A list of mapping references for that ontology", "type": "array", - "items": { "$ref": "https://masterthesis.rightsrestricted.com/ontohub/references.json#/definitions/mapping" } + "items": { "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#/definitions/mapping" } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:46 GMT + recorded_at: Wed, 13 Jan 2016 13:18:06 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -69,31 +93,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:46 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:06 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1120-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 85e7fa1b3fcca65983a01d09666e8683b356c06f + Expires: + - Wed, 13 Jan 2016 13:23:06 GMT + Source-Age: + - '15' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -104,11 +152,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -119,11 +163,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -134,11 +174,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -149,11 +185,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -164,11 +196,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -179,11 +207,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -194,22 +218,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -220,11 +236,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -235,11 +247,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -250,11 +258,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -265,37 +269,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -303,33 +294,185 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:46 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:18:06 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:18:06 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1128-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3a69375bce9f77ecae34e5d6a69811bf74781099 + Expires: + - Wed, 13 Jan 2016 13:23:06 GMT + Source-Age: + - '16' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:18:06 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/ontology_versions.yml b/spec/fixtures/vcr/api/json-schemata/ontology/ontology_versions.yml index fcc0179e9..89b26bab0 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/ontology_versions.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/ontology_versions.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/ontology_versions.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/ontology_versions/GET/200.json body: encoding: US-ASCII string: '' @@ -18,42 +18,66 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:47 GMT - Content-Type: - - application/json - Content-Length: - - '370' - Last-Modified: - - Sun, 08 Mar 2015 20:26:58 GMT - Connection: - - keep-alive - Etag: - - '"54fcb092-172"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"7388637fab13de012fe0197e83b55da6f88bb6dc"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1218:0F85:339D27:56964D45 + Content-Length: + - '241' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:38 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1120-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - d8bfa1db4cccadc1c21d7d38db7758e9ca0c6d53 + Expires: + - Wed, 13 Jan 2016 13:17:38 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/ontology_versions.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/ontology_versions/GET/200.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "///ontology_versions", "description": "A list of ontology version references for that ontology", "type": "array", - "items": { "$ref": "../../references.json#/definitions/ontology_version" } + "items": { "$ref": "../../../references.json#/definitions/ontology_version" } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:47 GMT + recorded_at: Wed, 13 Jan 2016 13:12:38 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -69,31 +93,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:48 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:38 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1131-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 935a8f5b5614ee864769f865801101e59a8d10bc + Expires: + - Wed, 13 Jan 2016 13:17:38 GMT + Source-Age: + - '3' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -104,11 +152,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -119,11 +163,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -134,11 +174,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -149,11 +185,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -164,11 +196,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -179,11 +207,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -194,22 +218,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -220,11 +236,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -235,11 +247,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -250,11 +258,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -265,37 +269,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -303,33 +294,185 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:48 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:12:38 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:12:38 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1126-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '2' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - d63c984c54922eb237f0517925896e59082ec883 + Expires: + - Wed, 13 Jan 2016 13:17:38 GMT + Source-Age: + - '4' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:12:38 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt.yml b/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt.yml index a4acb8327..5fb60f2a3 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/proof_attempt.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_attempt.json body: encoding: US-ASCII string: '' @@ -18,105 +18,158 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Thu, 09 Apr 2015 13:36:30 GMT - Content-Type: - - application/json - Content-Length: - - '2165' - Last-Modified: - - Thu, 09 Apr 2015 12:13:31 GMT - Connection: - - keep-alive - Etag: - - '"55266ceb-875"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"d3146ae2934c6ece29f254c9dbae6d6dc51be228"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1219:0F85:339715:56964D41 + Content-Length: + - '673' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:33 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1121-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - f13024ddcfc00b56d87ecf0930fc50eabd8d0078 + Expires: + - Wed, 13 Jan 2016 13:17:33 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/proof_attempt.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_attempt.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Proof Attempt", "description": "A proof attempt of a theorem", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the proof attempt", - "type": "string", - "format": "uri" - }, - "number": { - "description": "The number of this proof attempt in relation to the theorem", - "type": "integer" - }, - "used_prover": { - "description": "The name of the prover used during this attempt", - "type": "string" - }, - "tactic_script": { "$ref": "https://masterthesis.rightsrestricted.com/ontohub/tactic_script.json" }, - "prover_output": { - "oneOf": [ - { "$ref": "references.json#/definitions/prover_output" }, - { "type": "null" } + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The number of this proof attempt in relation to the theorem", + "type": "integer" + }, + "used_prover": { + "description": "The name of the prover used during this attempt", + "type": "string" + }, + "tactic_script": { "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/tactic_script.json" }, + "prover_output": { + "oneOf": [ + { "$ref": "references.json#/definitions/prover_output" }, + { "type": "null" } + ] + }, + "time_taken": { + "description": "The time it took to perform this proof attempt (in seconds)", + "type": ["integer", "null"] + }, + "evaluation_state": { + "description": "The current state of the proof evaluation", + "type": "string", + "enum": [ "done", "failed", "pending", "processing" ] + }, + "theorem": { "$ref": "references.json#/definitions/theorem" }, + "proof_attempt_configuration": { + "oneOf": [ + { "$ref": "references.json#/definitions/proof_attempt_configuration" }, + { "type": "null" } + ] + }, + "proof_status": { "$ref": "references.json#/definitions/proof_status" }, + "used_axioms": { + "description": "Reference to all axioms used for this attempt", + "type": "string", + "format": "uri" + }, + "used_theorems": { + "description": "Reference to all theorems used for this attempt", + "type": "string", + "format": "uri" + }, + "generated_axioms": { + "description": "Reference to all axioms generated during this attempt", + "type": "string", + "format": "uri" + } + }, + "required": [ + "iri", + "number", + "evaluation_state", + "theorem", + "proof_status", + "used_axioms", + "used_theorems", + "generated_axioms" ] }, - "time_taken": { - "description": "The time it took to perform this proof attempt (in seconds)", - "type": ["integer", "null"] - }, - "evaluation_state": { - "description": "The current state of the proof evaluation", - "type": "string", - "enum": [ "done", "failed", "pending", "processing" ] - }, - "theorem": { "$ref": "references.json#/definitions/theorem" }, - "proof_attempt_configuration": { - "oneOf": [ - { "$ref": "references.json#/definitions/proof_attempt_configuration" }, - { "type": "null" } + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The number of this proof attempt in relation to the theorem", + "type": "integer" + }, + "used_prover": { + "description": "The name of the prover used during this attempt", + "type": "string" + }, + "time_taken": { + "description": "The time it took to perform this proof attempt (in seconds)", + "type": ["integer", "null"] + }, + "evaluation_state": { + "description": "The current state of the proof evaluation", + "type": "string", + "enum": [ "done", "failed", "pending", "processing" ] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "number", + "evaluation_state", + "_links" ] - }, - "proof_status": { "$ref": "references.json#/definitions/proof_status" }, - "used_axioms": { - "description": "Reference to all axioms used for this attempt", - "type": "string", - "format": "uri" - }, - "used_theorems": { - "description": "Reference to all theorems used for this attempt", - "type": "string", - "format": "uri" - }, - "generated_axioms": { - "description": "Reference to all axioms generated during this attempt", - "type": "string", - "format": "uri" } - }, - "required": [ - "iri", - "number", - "evaluation_state", - "theorem", - "proof_status", - "used_axioms", - "used_theorems", - "generated_axioms" ] } http_version: - recorded_at: Thu, 09 Apr 2015 13:36:30 GMT + recorded_at: Wed, 13 Jan 2016 13:12:33 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/tactic_script.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -132,31 +185,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Thu, 09 Apr 2015 13:36:30 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 Content-Length: - - '623' - Last-Modified: - - Thu, 09 Apr 2015 12:31:34 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:12:33 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"55267126-26f"' + X-Served-By: + - cache-lcy1122-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 404dd42e3df5627e9e3e9c4e78c37759c2acac1d + Expires: + - Wed, 13 Jan 2016 13:17:33 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:12:34 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/tactic_script.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"daab1974009f77367b5fa9e407d0c944c1096681"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121A:0F87:3C70C9:56964D41 + Content-Length: + - '318' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:34 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1122-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - e59b8b957a613f5b3b7c73add19f8360f1e70ed2 + Expires: + - Wed, 13 Jan 2016 13:17:34 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/tactic_script.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/tactic_script.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Tactic Script", "description": "The tactic script generated in a proof attempt", @@ -178,10 +389,10 @@ http_interactions: "required": [ "time_limit" ] } http_version: - recorded_at: Thu, 09 Apr 2015 13:36:30 GMT + recorded_at: Wed, 13 Jan 2016 13:12:34 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -197,31 +408,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Thu, 09 Apr 2015 13:36:30 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:35 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1120-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 5e1b0b44aa55c64e8ba7a2ef7e26fcc070f3f6bc + Expires: + - Wed, 13 Jan 2016 13:17:35 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -232,11 +467,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -247,11 +478,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -262,11 +489,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -277,11 +500,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -292,11 +511,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -307,11 +522,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -322,22 +533,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -348,11 +551,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -363,11 +562,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -378,11 +573,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -393,37 +584,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -431,33 +609,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Thu, 09 Apr 2015 13:36:30 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:12:35 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt_configuration.yml b/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt_configuration.yml index fe29b0b8f..719d5becd 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt_configuration.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/proof_attempt_configuration.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/proof_attempt_configuration.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_attempt_configuration.json body: encoding: US-ASCII string: '' @@ -18,81 +18,125 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:36:26 GMT - Content-Type: - - application/json - Content-Length: - - '1376' - Last-Modified: - - Wed, 01 Apr 2015 09:28:42 GMT - Connection: - - keep-alive - Etag: - - '"551bba4a-560"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"e8659f1b7994d44bf530230287af886570244faa"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1222:3779:274690:56964E14 + Content-Length: + - '528' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:04 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 7df52b9d3d88eef820adf6a2a22ab87c609160a9 + Expires: + - Wed, 13 Jan 2016 13:21:04 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/proof_attempt_configuration.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_attempt_configuration.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Proof Attempt Configuration", "description": "A configuration of a proof attempt", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the proof attempt", - "type": "string", - "format": "uri" - }, - "number": { - "description": "The identifying number of the configuration. The number is relative to the ontology", - "type": "integer" - }, - "selected_prover": { - "description": "The name of the prover", - "type": ["string", "null"] - }, - "selected_timeout": { - "description": "The timeout for the proof attempt (in seconds)", - "type": ["integer", "null"] - }, - "selected_logic_mapping": { - "oneOf": [ - { "$ref": "references.json#/definitions/logic_mapping" }, - { "type": "null" } + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The identifying number of the configuration. The number is relative to the ontology", + "type": "integer" + }, + "selected_prover": { + "description": "The name of the prover", + "type": ["string", "null"] + }, + "selected_timeout": { + "description": "The timeout for the proof attempt (in seconds)", + "type": ["integer", "null"] + }, + "selected_logic_mapping": { + "oneOf": [ + { "$ref": "references.json#/definitions/logic_mapping" }, + { "type": "null" } + ] + }, + "selected_axioms": { + "description": "Reference to all axioms selected for this attempt", + "$ref": "generic/definitions.json#/definitions/iri" + }, + "selected_theorems": { + "description": "Reference to all theorems selected for this attempt", + "$ref": "generic/definitions.json#/definitions/iri" + } + }, + "required": [ + "iri", + "selected_axioms", + "selected_theorems" ] }, - "selected_axioms": { - "description": "Reference to all axioms selected for this attempt", - "type": "string", - "format": "uri" - }, - "selected_theorems": { - "description": "Reference to all theorems selected for this attempt", - "type": "string", - "format": "uri" + + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The identifying number of the configuration. The number is relative to the ontology", + "type": "integer" + }, + "selected_prover": { + "description": "The name of the prover", + "type": ["string", "null"] + }, + "selected_timeout": { + "description": "The timeout for the proof attempt (in seconds)", + "type": ["integer", "null"] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "_links" + ] } - }, - "required": [ - "iri", - "selected_axioms", - "selected_theorems" ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:36:26 GMT + recorded_at: Wed, 13 Jan 2016 13:16:04 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -108,31 +152,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:36:26 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:16:04 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1123-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 7f7556e3582ed5ff1a52525743ac858dcf972c11 + Expires: + - Wed, 13 Jan 2016 13:21:04 GMT + Source-Age: + - '211' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:16:04 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:04 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1122-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3d8760814e728957bb8f4fa58f449b899261992b + Expires: + - Wed, 13 Jan 2016 13:21:04 GMT + Source-Age: + - '210' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -143,11 +345,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -158,11 +356,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -173,11 +367,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -188,11 +378,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -203,11 +389,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -218,11 +400,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -233,22 +411,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -259,11 +429,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -274,11 +440,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -289,11 +451,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -304,37 +462,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -342,33 +487,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:36:26 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:16:04 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/prover_output.yml b/spec/fixtures/vcr/api/json-schemata/ontology/prover_output.yml index 46cef1f95..a69cac1e7 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/prover_output.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/prover_output.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/prover_output.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/prover_output.json body: encoding: US-ASCII string: '' @@ -18,63 +18,107 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:03 GMT - Content-Type: - - application/json - Content-Length: - - '760' - Last-Modified: - - Wed, 01 Apr 2015 09:31:10 GMT - Connection: - - keep-alive - Etag: - - '"551bbade-2f8"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"ac064a261ccafb6c0e83455a8b09c2a85e606942"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1221:377A:2FD826:56964E91 + Content-Length: + - '369' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:09 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1134-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 9f10bdc1bd75866fdca9da71cd8f31a38848df37 + Expires: + - Wed, 13 Jan 2016 13:23:09 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/prover_output.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/prover_output.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Prover Output", "description": "The prover output for a specific proof attempt", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the prover output", - "type": "string", - "format": "uri" - }, - "prover": { - "description": "The name of the prover", - "type": "string" - }, - "content": { - "description": "The actual prover output", - "type": "string" + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "prover": { + "description": "The name of the prover", + "type": "string" + }, + "content": { + "description": "The actual prover output", + "type": "string" + }, + "proof_attempt": { "$ref": "references.json#/definitions/proof_attempt" } + }, + "required": [ + "iri", + "prover", + "content", + "proof_attempt" + ] }, - "proof_attempt": { "$ref": "references.json#/definitions/proof_attempt" } - }, - "required": [ - "iri", - "prover", - "content", - "proof_attempt" + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "prover": { + "description": "The name of the prover", + "type": "string" + }, + "content": { + "description": "The actual prover output", + "type": "string" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "prover", + "content", + "_links" + ] + } ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:03 GMT + recorded_at: Wed, 13 Jan 2016 13:18:09 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -90,31 +134,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:03 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:18:09 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '2' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 609d267deeba7fa3e65e88313b5ea4eb071477ab + Expires: + - Wed, 13 Jan 2016 13:23:09 GMT + Source-Age: + - '19' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:18:09 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:18:09 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1124-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '2' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 5685855f15bb11d95a473a51e90779ea45e08d6b + Expires: + - Wed, 13 Jan 2016 13:23:09 GMT + Source-Age: + - '19' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -125,11 +327,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -140,11 +338,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -155,11 +349,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -170,11 +360,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -185,11 +371,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -200,11 +382,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -215,22 +393,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -241,11 +411,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -256,11 +422,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -271,11 +433,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -286,37 +444,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -324,33 +469,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:03 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:18:09 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/sentences.yml b/spec/fixtures/vcr/api/json-schemata/ontology/sentences.yml index d04ff01d1..effde49ff 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/sentences.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/sentences.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/sentences.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/sentences/GET/200.json body: encoding: US-ASCII string: '' @@ -18,42 +18,66 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:43 GMT - Content-Type: - - application/json - Content-Length: - - '382' - Last-Modified: - - Mon, 09 Mar 2015 09:05:59 GMT - Connection: - - keep-alive - Etag: - - '"54fd6277-17e"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"ed411fc1923181c2f3d1e9cd8e73695d11d6c9cd"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1221:377C:3C579E:56964E80 + Content-Length: + - '231' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:52 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1124-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - a248671904705b49ee4a69be971e23107b5a9248 + Expires: + - Wed, 13 Jan 2016 13:22:52 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/sentences.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/sentences/GET/200.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "///sentences", "description": "A list of sentence references for that ontology", "type": "array", - "items": { "$ref": "https://masterthesis.rightsrestricted.com/ontohub/references.json#/definitions/sentence" } + "items": { "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#/definitions/sentence" } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:43 GMT + recorded_at: Wed, 13 Jan 2016 13:17:52 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -69,31 +93,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:43 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:52 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1123-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3bb51c526d6fe71df59a08d3aebdd3b101f0c23d + Expires: + - Wed, 13 Jan 2016 13:22:52 GMT + Source-Age: + - '1' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -104,11 +152,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -119,11 +163,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -134,11 +174,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -149,11 +185,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -164,11 +196,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -179,11 +207,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -194,22 +218,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -220,11 +236,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -235,11 +247,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -250,11 +258,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -265,37 +269,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -303,33 +294,185 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:43 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:17:52 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:17:52 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1127-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 8c7459610aeea0c0cd02c4566693fc7caf36973e + Expires: + - Wed, 13 Jan 2016 13:22:52 GMT + Source-Age: + - '2' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:17:52 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/symbols.yml b/spec/fixtures/vcr/api/json-schemata/ontology/symbols.yml index e9b9d2fd5..5806ff19c 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/symbols.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/symbols.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/symbols.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/symbols/GET/200.json body: encoding: US-ASCII string: '' @@ -18,42 +18,66 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:34 GMT - Content-Type: - - application/json - Content-Length: - - '374' - Last-Modified: - - Mon, 09 Mar 2015 09:40:30 GMT - Connection: - - keep-alive - Etag: - - '"54fd6a8e-176"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"51aa6389bd025583abb7773937a1141be1f33136"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1217:59C5:3C1F61:56964E04 + Content-Length: + - '233' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:15:48 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 7df0ec0f7700d3bd19b64d3d0531ddaafe9f8ed2 + Expires: + - Wed, 13 Jan 2016 13:20:48 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology/commands/symbols.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology/symbols/GET/200.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "///symbols", "description": "A list of symbol references for that ontology", "type": "array", - "items": { "$ref": "https://masterthesis.rightsrestricted.com/ontohub/references.json#/definitions/symbol" } + "items": { "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#/definitions/symbol" } } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:34 GMT + recorded_at: Wed, 13 Jan 2016 13:15:48 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json body: encoding: US-ASCII string: '' @@ -69,31 +93,55 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:34 GMT - Content-Type: - - application/json - Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT - Connection: - - keep-alive - Etag: - - '"551d13d1-18df"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:15:48 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1128-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '4' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 229fa579a4ac751f1b5a3a76bb6be0ea14d012fd + Expires: + - Wed, 13 Jan 2016 13:20:48 GMT + Source-Age: + - '194' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -104,11 +152,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -119,11 +163,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -134,11 +174,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -149,11 +185,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -164,11 +196,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -179,11 +207,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -194,22 +218,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -220,11 +236,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -235,11 +247,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -250,11 +258,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -265,37 +269,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -303,33 +294,185 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:34 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:15:48 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:15:49 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1129-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '3' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - cbb97af2b17537d16581a3eb01ed5513d1573d55 + Expires: + - Wed, 13 Jan 2016 13:20:49 GMT + Source-Age: + - '195' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:15:49 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology/theorem.yml b/spec/fixtures/vcr/api/json-schemata/ontology/theorem.yml index ab1a41f9c..a4f46080e 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology/theorem.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology/theorem.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/theorem.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/theorem.json body: encoding: US-ASCII string: '' @@ -18,65 +18,111 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:35:18 GMT - Content-Type: - - application/json - Content-Length: - - '1379' - Last-Modified: - - Thu, 02 Apr 2015 10:03:34 GMT - Connection: - - keep-alive - Etag: - - '"551d13f6-563"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"aaf74a4383a6a6aa3f3a84e71b478332444619e6"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B14:59C7:42E93E:56964E36 + Content-Length: + - '551' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:39 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4147-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3d94f561edc766722f248777ca6bc84a0790942e + Expires: + - Wed, 13 Jan 2016 13:21:39 GMT + Source-Age: + - '0' body: encoding: ASCII-8BIT string: !binary |- - ewogICJpZCI6ICJodHRwczovL21hc3RlcnRoZXNpcy5yaWdodHNyZXN0cmlj - dGVkLmNvbS9vbnRvaHViL3RoZW9yZW0uanNvbiMiLAogICIkc2NoZW1hIjog - Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIyIsCiAg - InRpdGxlIjogIlRoZW9yZW0iLAogICJkZXNjcmlwdGlvbiI6ICJBIHRoZW9y - ZW0gaW5zaWRlIG9mIGFuIG9udG9sb2d5IiwKICAidHlwZSI6ICJvYmplY3Qi - LAogICJwcm9wZXJ0aWVzIjogewogICAgImlyaSI6IHsKICAgICAgImRlc2Ny - aXB0aW9uIjogIlRoZSB1bmlxdWUgaWRlbnRpZmllciBmb3IgdGhlIHRoZW9y - ZW0iLAogICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAiZm9ybWF0Ijog - InVyaSIKICAgIH0sCiAgICAibmFtZSI6IHsKICAgICAgImRlc2NyaXB0aW9u - IjogIlRoZSBuYW1lIG9mIHRoZSB0aGVvcmVtIOKAlCB1c3VhbGx5IGF1dG8t - Z2VuZXJhdGVkLiIsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAg - ICAiZGVmaW5pdGlvbiI6IHsKICAgICAgImRlc2NyaXB0aW9uIjogIlRoZSBk - ZWZpbml0aW9uIG9mIHRoZSB0aGVvcmVtIiwKICAgICAgInR5cGUiOiAic3Ry - aW5nIgogICAgfSwKICAgICJldmFsdWF0aW9uX3N0YXRlIjogewogICAgICAi - ZGVzY3JpcHRpb24iOiAiVGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHByb29m - IGV2YWx1YXRpb24iLAogICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAi - ZW51bSI6IFsgImRvbmUiLCAiZmFpbGVkIiwgInBlbmRpbmciLCAicHJvY2Vz - c2luZyIgXQogICAgfSwKICAgICJwcm9vZl9zdGF0dXMiOiB7ICIkcmVmIjog - InJlZmVyZW5jZXMuanNvbiMvZGVmaW5pdGlvbnMvcHJvb2Zfc3RhdHVzIiB9 - LAogICAgIm9udG9sb2d5IjogeyAiJHJlZiI6ICJyZWZlcmVuY2VzLmpzb24j - L2RlZmluaXRpb25zL29udG9sb2d5IiB9LAogICAgInN5bWJvbHMiOiB7CiAg - ICAgICJkZXNjcmlwdGlvbiI6ICJSZWZlcmVuY2UgdG8gYWxsIHN5bWJvbHMg - dXNlZCBpbiB0aGlzIHRoZW9yZW0iLAogICAgICAidHlwZSI6ICJzdHJpbmci - LAogICAgICAiZm9ybWF0IjogInVyaSIKICAgIH0sCiAgICAicHJvb2ZfYXR0 - ZW1wdHMiOiB7CiAgICAgICJkZXNjcmlwdGlvbiI6ICJSZWZlcmVuY2UgdG8g - YWxsIHByb29mIGF0dGVtcHRzIGZvciB0aGlzIHRoZW9yZW0iLAogICAgICAi - dHlwZSI6ICJzdHJpbmciLAogICAgICAiZm9ybWF0IjogInVyaSIKICAgIH0K - ICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJpcmkiLAogICAgIm5hbWUiLAog - ICAgImRlZmluaXRpb24iLAogICAgImV2YWx1YXRpb25fc3RhdGUiLAogICAg - InByb29mX3N0YXR1cyIsCiAgICAib250b2xvZ3kiLAogICAgInN5bWJvbHMi - LAogICAgInByb29mX2F0dGVtcHRzIgogIF0KfQo= + ewogICJpZCI6ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20v + b250b2h1Yi9vbnRvaHViLWFwaS1qc29uL2RldmVsb3AvdGhlb3JlbS5qc29u + IyIsCiAgIiRzY2hlbWEiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFm + dC0wNC9zY2hlbWEjIiwKICAidGl0bGUiOiAiVGhlb3JlbSIsCiAgImRlc2Ny + aXB0aW9uIjogIkEgdGhlb3JlbSBpbnNpZGUgb2YgYW4gb250b2xvZ3kiLAog + ICJ0eXBlIjogIm9iamVjdCIsCiAgIm9uZU9mIjogWwogICAgewogICAgICAi + cHJvcGVydGllcyI6IHsKICAgICAgICAiaXJpIjogeyAiJHJlZiI6ICJnZW5l + cmljL2RlZmluaXRpb25zLmpzb24jL2RlZmluaXRpb25zL2lyaSIgfSwKICAg + ICAgICAibmFtZSI6IHsKICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJUaGUg + bmFtZSBvZiB0aGUgdGhlb3JlbSDigJQgdXN1YWxseSBhdXRvLWdlbmVyYXRl + ZC4iLAogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAg + ICAgICAgImRlZmluaXRpb24iOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb24i + OiAiVGhlIGRlZmluaXRpb24gb2YgdGhlIHRoZW9yZW0iLAogICAgICAgICAg + InR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImV2YWx1YXRp + b25fc3RhdGUiOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIGN1 + cnJlbnQgc3RhdGUgb2YgdGhlIHByb29mIGV2YWx1YXRpb24iLAogICAgICAg + ICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJlbnVtIjogWyAibm90 + X3N0YXJ0ZWRfeWV0IiwgImZhaWxlZCIsICJub19yZXN1bHQiLCAicGVuZGlu + ZyIsICJwcm9jZXNzaW5nIiwgImRvbmUiIF0KICAgICAgICB9LAogICAgICAg + ICJwcm9vZl9zdGF0dXMiOiB7ICIkcmVmIjogInJlZmVyZW5jZXMuanNvbiMv + ZGVmaW5pdGlvbnMvcHJvb2Zfc3RhdHVzIiB9LAogICAgICAgICJvbnRvbG9n + eSI6IHsgIiRyZWYiOiAicmVmZXJlbmNlcy5qc29uIy9kZWZpbml0aW9ucy9v + bnRvbG9neSIgfSwKICAgICAgICAic3ltYm9scyI6IHsKICAgICAgICAgICJk + ZXNjcmlwdGlvbiI6ICJSZWZlcmVuY2UgdG8gYWxsIHN5bWJvbHMgdXNlZCBp + biB0aGlzIHRoZW9yZW0iLAogICAgICAgICAgIiRyZWYiOiAiZ2VuZXJpYy9k + ZWZpbml0aW9ucy5qc29uIy9kZWZpbml0aW9ucy9pcmkiCiAgICAgICAgfSwK + ICAgICAgICAicHJvb2ZfYXR0ZW1wdHMiOiB7CiAgICAgICAgICAiZGVzY3Jp + cHRpb24iOiAiUmVmZXJlbmNlIHRvIGFsbCBwcm9vZiBhdHRlbXB0cyBmb3Ig + dGhpcyB0aGVvcmVtIiwKICAgICAgICAgICIkcmVmIjogImdlbmVyaWMvZGVm + aW5pdGlvbnMuanNvbiMvZGVmaW5pdGlvbnMvaXJpIgogICAgICAgIH0KICAg + ICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJpcmkiLAogICAg + ICAgICJuYW1lIiwKICAgICAgICAiZGVmaW5pdGlvbiIsCiAgICAgICAgImV2 + YWx1YXRpb25fc3RhdGUiLAogICAgICAgICJwcm9vZl9zdGF0dXMiLAogICAg + ICAgICJvbnRvbG9neSIsCiAgICAgICAgInN5bWJvbHMiLAogICAgICAgICJw + cm9vZl9hdHRlbXB0cyIKICAgICAgXQogICAgfSwKICAgIHsKICAgICAgInBy + b3BlcnRpZXMiOiB7CiAgICAgICAgImlyaSI6IHsgIiRyZWYiOiAiZ2VuZXJp + Yy9kZWZpbml0aW9ucy5qc29uIy9kZWZpbml0aW9ucy9pcmkiIH0sCiAgICAg + ICAgIm5hbWUiOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIG5h + bWUgb2YgdGhlIHRoZW9yZW0g4oCUIHVzdWFsbHkgYXV0by1nZW5lcmF0ZWQu + IiwKICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICB9LAogICAg + ICAgICJkZWZpbml0aW9uIjogewogICAgICAgICAgImRlc2NyaXB0aW9uIjog + IlRoZSBkZWZpbml0aW9uIG9mIHRoZSB0aGVvcmVtIiwKICAgICAgICAgICJ0 + eXBlIjogInN0cmluZyIKICAgICAgICB9LAogICAgICAgICJldmFsdWF0aW9u + X3N0YXRlIjogewogICAgICAgICAgImRlc2NyaXB0aW9uIjogIlRoZSBjdXJy + ZW50IHN0YXRlIG9mIHRoZSBwcm9vZiBldmFsdWF0aW9uIiwKICAgICAgICAg + ICJ0eXBlIjogInN0cmluZyIsCiAgICAgICAgICAiZW51bSI6IFsgImRvbmUi + LCAiZmFpbGVkIiwgInBlbmRpbmciLCAicHJvY2Vzc2luZyIgXQogICAgICAg + IH0sCiAgICAgICAgIl9saW5rcyI6IHsgIiRyZWYiOiAiZ2VuZXJpYy9kZWZp + bml0aW9ucy5qc29uIy9kZWZpbml0aW9ucy9fbGlua3MiIH0KICAgICAgfSwK + ICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJpcmkiLAogICAgICAgICJu + YW1lIiwKICAgICAgICAiZGVmaW5pdGlvbiIsCiAgICAgICAgImV2YWx1YXRp + b25fc3RhdGUiLAogICAgICAgICJfbGlua3MiCiAgICAgIF0KICAgIH0KICBd + Cn0K http_version: - recorded_at: Sat, 04 Apr 2015 19:35:18 GMT + recorded_at: Wed, 13 Jan 2016 13:16:39 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -92,31 +138,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:35:18 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B23:2C5B:3A6A7C:56964E23 Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:16:39 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-ams4144-AMS + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 9f8c39af67839d211025030e33cb310086e776c7 + Expires: + - Wed, 13 Jan 2016 13:21:39 GMT + Source-Age: + - '19' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:16:39 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B25:2C57:252DA3:56964E24 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:16:39 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4141-AMS + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 4d45107c0d4e4358fe8334020f1da8df79f0e6c4 + Expires: + - Wed, 13 Jan 2016 13:21:39 GMT + Source-Age: + - '19' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -127,11 +331,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -142,11 +342,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -157,11 +353,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -172,11 +364,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -187,11 +375,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -202,11 +386,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -217,22 +397,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -243,11 +415,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -258,11 +426,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -273,11 +437,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -288,37 +448,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -326,33 +473,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:35:18 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:16:39 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology_type.yml b/spec/fixtures/vcr/api/json-schemata/ontology_type.yml index 06cd337e1..3e25ddde3 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology_type.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology_type.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology_type.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology_type.json body: encoding: US-ASCII string: '' @@ -18,59 +18,234 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:57 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"78d563e7c07b024fbba30fffef65e81465595714"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1216:377F:43A5BA:56964E82 Content-Length: - - '815' - Last-Modified: - - Thu, 26 Mar 2015 10:32:13 GMT + - '411' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:17:54 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"5513e02d-32f"' + X-Served-By: + - cache-lcy1134-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3f7781f7157a498c55ef52e1fa40fa9ea135297a + Expires: + - Wed, 13 Jan 2016 13:22:54 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology_type.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Ontology Type", + "description": "A specific type of an ontology", + "type": "object", + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the formality level", + "type": "string" + }, + "description": { + "description": "A description of the formality level", + "type": ["string", "null"] + }, + "documentation_url": { + "description": "A url to specific documentation for this type", + "type": ["string", "null"], + "format": "uri" + } + }, + "required": [ + "iri", "name" + ] + }, + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the ontology type", + "type": "string" + }, + "description": { + "description": "A description of the ontology type", + "type": ["string", "null"] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "name", "_links" + ] + } + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:17:54 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E + Content-Length: + - '947' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:55 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1127-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '2' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 02e9552dc88d77acb0b1eae230ff0840cbc232af + Expires: + - Wed, 13 Jan 2016 13:22:55 GMT + Source-Age: + - '4' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology_type.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Ontology Type", - "description": "A specific type of an ontology", - "type": "object", - "properties": { + "definitions": { "iri": { - "description": "The unique identifier for an ontology type", + "description": "The IRI of a resource", "type": "string", - "format": "uri" - }, - "name": { - "description": "The name of the formality level", - "type": "string" + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" }, - "description": { - "description": "A description of the formality level", - "type": ["string", "null"] + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } }, - "documentation_url": { - "description": "A url to specific documentation for this type", - "type": ["string", "null"], - "format": "uri" + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } } - }, - "required": [ - "iri", "name" - ] + } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:57 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:17:55 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/ontology_version.yml b/spec/fixtures/vcr/api/json-schemata/ontology_version.yml index 522575997..87608b925 100644 --- a/spec/fixtures/vcr/api/json-schemata/ontology_version.yml +++ b/spec/fixtures/vcr/api/json-schemata/ontology_version.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/ontology_version.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology_version.json body: encoding: US-ASCII string: '' @@ -18,75 +18,131 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:48 GMT - Content-Type: - - application/json - Content-Length: - - '1276' - Last-Modified: - - Thu, 26 Mar 2015 10:32:21 GMT - Connection: - - keep-alive - Etag: - - '"5513e035-4fc"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"1fd2248463a0c00fff939577fabb218936a7bd75"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1222:0F89:3D9B47:56964D44 + Content-Length: + - '510' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:37 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1127-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 7999c9c20f7f637ae7bda89cadc89ea05dcbf64d + Expires: + - Wed, 13 Jan 2016 13:17:37 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/ontology_version.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/ontology_version.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Ontology Version", "description": "An ontology version of an ontology", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for an ontology version", - "type": "string", - "format": "uri" - }, - "number": { - "description": "The version number of the version of the ontology", - "type": "integer" - }, - "evaluation_state": { - "description": "The evaluation state of this ontology version", - "type": "string" - }, - "commit_oid": { - "description": "The commit oid of the commit that triggered this ontology version", - "type": "string" - }, - "basepath": { - "description": "The path to the ontology relative to the git repositories root directory", - "type": "string" - }, - "file_extension": { - "description": "Actual file extension of the ontology file in the repository", - "type": "string" + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The version number of the version of the ontology", + "type": "integer" + }, + "evaluation_state": { + "description": "The evaluation state of this ontology version", + "type": "string" + }, + "commit_oid": { + "description": "The commit oid of the commit that triggered this ontology version", + "type": "string" + }, + "basepath": { + "description": "The path to the ontology relative to the git repositories root directory", + "type": "string" + }, + "file_extension": { + "description": "Actual file extension of the ontology file in the repository", + "type": "string" + }, + "ontology": { "$ref": "references.json#/definitions/ontology" } + }, + "required": [ + "iri", "number", + "commit_oid", "evaluation_state", + "basepath", "file_extension", + "ontology" + ] }, - "ontology": { "$ref": "references.json#/definitions/ontology" } - }, - "required": [ - "iri", "number", - "commit_oid", "evaluation_state", - "basepath", "file_extension", - "ontology" + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "number": { + "description": "The version number of the version of the ontology", + "type": "integer" + }, + "evaluation_state": { + "description": "The evaluation state of this ontology version", + "type": "string" + }, + "commit_oid": { + "description": "The commit oid of the commit that triggered this ontology version", + "type": "string" + }, + "basepath": { + "description": "The path to the ontology relative to the git repositories root directory", + "type": "string" + }, + "file_extension": { + "description": "Actual file extension of the ontology file in the repository", + "type": "string" + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", "number", + "commit_oid", "evaluation_state", + "basepath", "file_extension", + "_links" + ] + } ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:48 GMT + recorded_at: Wed, 13 Jan 2016 13:12:37 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -102,31 +158,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:48 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:12:37 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1126-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 00610c1d867a31819e458bbe00d7b1ebd4372d37 + Expires: + - Wed, 13 Jan 2016 13:17:37 GMT + Source-Age: + - '3' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:12:37 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:37 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1125-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - cd449081e21221dfb5e2f116820c08af114da5ee + Expires: + - Wed, 13 Jan 2016 13:17:37 GMT + Source-Age: + - '3' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -137,11 +351,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -152,11 +362,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -167,11 +373,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -182,11 +384,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -197,11 +395,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -212,11 +406,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -227,22 +417,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -253,11 +435,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -268,11 +446,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -283,11 +457,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -298,37 +468,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -336,33 +493,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:48 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:12:37 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/proof_status.yml b/spec/fixtures/vcr/api/json-schemata/proof_status.yml index 7ce581cdd..896111be7 100644 --- a/spec/fixtures/vcr/api/json-schemata/proof_status.yml +++ b/spec/fixtures/vcr/api/json-schemata/proof_status.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/proof_status.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_status.json body: encoding: US-ASCII string: '' @@ -18,41 +18,61 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:35:51 GMT - Content-Type: - - application/json - Content-Length: - - '803' - Last-Modified: - - Wed, 01 Apr 2015 09:12:04 GMT - Connection: - - keep-alive - Etag: - - '"551bb664-323"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"73dc623470c97f8b6560e03d2b585074a3df92bd"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1223:59C9:3AC9F6:56964E03 + Content-Length: + - '333' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:15:56 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - dea26e083238a9985bac1cd4c0aae07a6ef9f4a7 + Expires: + - Wed, 13 Jan 2016 13:20:56 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/proof_status.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/proof_status.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Proof-Status", "description": "proof status of a theorem or a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier for the proof status", - "type": "string", - "format": "uri" - }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, "identifier": { "description": "The SZS identifier of the proof status", "type": "string" @@ -64,7 +84,8 @@ http_interactions: "description": { "description": "Description of the proof status", "type": "string" - } + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } }, "required": [ "iri", @@ -74,5 +95,139 @@ http_interactions: ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:35:51 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:15:56 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:15:57 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 71d50d623fff0abc619ddd57898cc209a74028bd + Expires: + - Wed, 13 Jan 2016 13:20:57 GMT + Source-Age: + - '203' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:15:57 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/repository.yml b/spec/fixtures/vcr/api/json-schemata/repository.yml index 8957353bd..3265323de 100644 --- a/spec/fixtures/vcr/api/json-schemata/repository.yml +++ b/spec/fixtures/vcr/api/json-schemata/repository.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/repository.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/repository.json body: encoding: US-ASCII string: '' @@ -18,41 +18,61 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:20 GMT - Content-Type: - - application/json - Content-Length: - - '1840' - Last-Modified: - - Wed, 01 Apr 2015 09:38:39 GMT - Connection: - - keep-alive - Etag: - - '"551bbc9f-730"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"e022919a70fd58ca97bf15128171904517fd27fe"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121E:0F83:26312A:56964D49 + Content-Length: + - '583' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:12:41 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1123-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 1a42b7ef6656e206971c81a952bdff0660decbf7 + Expires: + - Wed, 13 Jan 2016 13:17:41 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/repository.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/repository.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Repository", "description": "A repository inside of Ontohub", "type": "object", "properties": { - "iri": { - "description": "The unique identifier for a repository", - "type": "string", - "format": "uri" - }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, "name": { "description": "The name of the repository", "type": "string" @@ -99,9 +119,144 @@ http_interactions: }, { "type": "null" } ] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:12:42 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:12:42 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1120-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - b2f8a36dbef27ca6780a2cb461b1d3604bd2ba33 + Expires: + - Wed, 13 Jan 2016 13:17:42 GMT + Source-Age: + - '8' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:20 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:12:42 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/repository/combinations/202.yml b/spec/fixtures/vcr/api/json-schemata/repository/combinations/202.yml new file mode 100644 index 000000000..ffe750bb5 --- /dev/null +++ b/spec/fixtures/vcr/api/json-schemata/repository/combinations/202.yml @@ -0,0 +1,226 @@ +--- +http_interactions: +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/202.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"e1e23d8fd8c3a9988d78d8154742c853c538c5a1"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B26:2C59:34E346:56964F7C + Content-Length: + - '374' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:04 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4144-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 3132d2cc5ceeff78eae2c401db3780598e1e866c + Expires: + - Wed, 13 Jan 2016 13:27:04 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/202.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "202 (Accepted)", + "description": "This is a generic 202 (Accepted) response", + "type": "object", + "properties": { + "status": { + "description": "The human-readable description of the 202 status.", + "type": "string", + "enum": [ "Accepted" ] + }, + "location": { + "description": "Reference to the intermediately created resource. Mirrors the location-Header of the response.", + "type": "string" + }, + "_links": { "$ref": "definitions.json#/definitions/_links" } + }, + "required": [ + "status", "location" + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:04 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B23:2C5C:4019BD:56964F7C + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:04 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4144-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - d4dbb31c52891a25c199693e2d058521084cbdad + Expires: + - Wed, 13 Jan 2016 13:27:04 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:04 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/repository/combinations/400.yml b/spec/fixtures/vcr/api/json-schemata/repository/combinations/400.yml new file mode 100644 index 000000000..d8c3838af --- /dev/null +++ b/spec/fixtures/vcr/api/json-schemata/repository/combinations/400.yml @@ -0,0 +1,223 @@ +--- +http_interactions: +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/400.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"12745d3bce15b8dcfc2244c061946ea0e738beb2"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B2C:2C5C:401FBD:56964F7F + Content-Length: + - '325' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:08 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4120-AMS + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 54f13d8ede321c11e8251514a5bb4bef18a60d3d + Expires: + - Wed, 13 Jan 2016 13:27:08 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/400.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "400 (Bad Request)", + "description": "This is a generic 400 (Bad Request) response", + "type": "object", + "properties": { + "status": { + "description": "The human-readable description of the 400 status.", + "type": "string", + "enum": [ "Bad Request" ] + }, + "errors": { "$ref": "definitions.json#/definitions/errors"}, + "_links": { "$ref": "definitions.json#/definitions/_links" } + }, + "required": [ + "status", "errors" + ] + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:08 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - 17EB2B23:2C5C:4019BD:56964F7C + Content-Length: + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:22:08 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-ams4124-AMS + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 24dad0ff53deea311eb961031abcf0d3e405799d + Expires: + - Wed, 13 Jan 2016 13:27:08 GMT + Source-Age: + - '4' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:22:08 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/sentence.yml b/spec/fixtures/vcr/api/json-schemata/sentence.yml index 6b9c5265d..8c760e49d 100644 --- a/spec/fixtures/vcr/api/json-schemata/sentence.yml +++ b/spec/fixtures/vcr/api/json-schemata/sentence.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/sentence.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/sentence.json body: encoding: US-ASCII string: '' @@ -18,58 +18,101 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:43 GMT - Content-Type: - - application/json - Content-Length: - - '1064' - Last-Modified: - - Mon, 30 Mar 2015 07:48:14 GMT - Connection: - - keep-alive - Etag: - - '"5518ffbe-428"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"0fd7e0d5da3c3130022437905b11410bd3c2dcac"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1217:3778:1DBD90:56964E7D + Content-Length: + - '466' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:50 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1134-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 27cc8cf5e237dfa205d45a94ebd24970c9eb188b + Expires: + - Wed, 13 Jan 2016 13:22:50 GMT + Source-Age: + - '0' body: encoding: ASCII-8BIT string: !binary |- - ewogICJpZCI6ICJodHRwczovL21hc3RlcnRoZXNpcy5yaWdodHNyZXN0cmlj - dGVkLmNvbS9vbnRvaHViL3NlbnRlbmNlLmpzb24jIiwKICAiJHNjaGVtYSI6 - ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMiLAog - ICJ0aXRsZSI6ICJTZW50ZW5jZSIsCiAgImRlc2NyaXB0aW9uIjogIkEgc2Vu - dGVuY2UgaW5zaWRlIG9mIGFuIG9udG9sb2d5IiwKICAidHlwZSI6ICJvYmpl - Y3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgImlyaSI6IHsKICAgICAgImRl - c2NyaXB0aW9uIjogIlRoZSB1bmlxdWUgaWRlbnRpZmllciBmb3IgdGhlIHNl - bnRlbmNlIiwKICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgImZvcm1h - dCI6ICJ1cmkiCiAgICB9LAogICAgIm5hbWUiOiB7CiAgICAgICJkZXNjcmlw - dGlvbiI6ICJUaGUgbmFtZSBvZiB0aGUgc2VudGVuY2Ug4oCUIHVzdWFsbHkg - YXV0by1nZW5lcmF0ZWQuIiwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAg - fSwKICAgICJkZWZpbml0aW9uIjogewogICAgICAiZGVzY3JpcHRpb24iOiAi - VGhlIGRlZmluaXRpb24gb2YgdGhlIHNlbnRlbmNlIiwKICAgICAgInR5cGUi - OiAic3RyaW5nIgogICAgfSwKICAgICJpbXBvcnRlZCI6IHsKICAgICAgImRl - c2NyaXB0aW9uIjogIkRlbm90ZXMgd2hldGhlciB0aGlzIHNlbnRlbmNlIHdh - cyBpbXBvcnRlZCBmcm9tIGFub3RoZXIgb250b2xvZ3kiLAogICAgICAidHlw - ZSI6ICJib29sZWFuIgogICAgfSwKICAgICJvbnRvbG9neSI6IHsgIiRyZWYi - OiAicmVmZXJlbmNlcy5qc29uIy9kZWZpbml0aW9ucy9vbnRvbG9neSIgfSwK - ICAgICJzeW1ib2xzIjogewogICAgICAiZGVzY3JpcHRpb24iOiAiUmVmZXJl - bmNlIHRvIGFsbCBzeW1ib2xzIHVzZWQgaW4gdGhpcyBzZW50ZW5jZSIsCiAg - ICAgICJ0eXBlIjogInN0cmluZyIsCiAgICAgICJmb3JtYXQiOiAidXJpIgog - ICAgfQogIH0sCiAgInJlcXVpcmVkIjogWwogICAgImlyaSIsCiAgICAibmFt - ZSIsCiAgICAiZGVmaW5pdGlvbiIsCiAgICAiaW1wb3J0ZWQiLAogICAgIm9u - dG9sb2d5IiwKICAgICJzeW1ib2xzIgogIF0KfQo= + ewogICJpZCI6ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20v + b250b2h1Yi9vbnRvaHViLWFwaS1qc29uL2RldmVsb3Avc2VudGVuY2UuanNv + biMiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJh + ZnQtMDQvc2NoZW1hIyIsCiAgInRpdGxlIjogIlNlbnRlbmNlIiwKICAiZGVz + Y3JpcHRpb24iOiAiQSBzZW50ZW5jZSBpbnNpZGUgb2YgYW4gb250b2xvZ3ki + LAogICJ0eXBlIjogIm9iamVjdCIsCiAgIm9uZU9mIjogWwogICAgewogICAg + ICAicHJvcGVydGllcyI6IHsKICAgICAgICAiaXJpIjogeyAiJHJlZiI6ICJn + ZW5lcmljL2RlZmluaXRpb25zLmpzb24jL2RlZmluaXRpb25zL2lyaSIgfSwK + ICAgICAgICAibmFtZSI6IHsKICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJU + aGUgbmFtZSBvZiB0aGUgc2VudGVuY2Ug4oCUIHVzdWFsbHkgYXV0by1nZW5l + cmF0ZWQuIiwKICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICB9 + LAogICAgICAgICJkZWZpbml0aW9uIjogewogICAgICAgICAgImRlc2NyaXB0 + aW9uIjogIlRoZSBkZWZpbml0aW9uIG9mIHRoZSBzZW50ZW5jZSIsCiAgICAg + ICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgfSwKICAgICAgICAiaW1w + b3J0ZWQiOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiRGVub3RlcyB3 + aGV0aGVyIHRoaXMgc2VudGVuY2Ugd2FzIGltcG9ydGVkIGZyb20gYW5vdGhl + ciBvbnRvbG9neSIsCiAgICAgICAgICAidHlwZSI6ICJib29sZWFuIgogICAg + ICAgIH0sCiAgICAgICAgIm9udG9sb2d5IjogeyAiJHJlZiI6ICJyZWZlcmVu + Y2VzLmpzb24jL2RlZmluaXRpb25zL29udG9sb2d5IiB9LAogICAgICAgICJz + eW1ib2xzIjogewogICAgICAgICAgImRlc2NyaXB0aW9uIjogIlJlZmVyZW5j + ZSB0byBhbGwgc3ltYm9scyB1c2VkIGluIHRoaXMgc2VudGVuY2UiLAogICAg + ICAgICAgIiRyZWYiOiAiZ2VuZXJpYy9kZWZpbml0aW9ucy5qc29uIy9kZWZp + bml0aW9ucy9pcmkiCiAgICAgICAgfQogICAgICB9LAogICAgICAicmVxdWly + ZWQiOiBbCiAgICAgICAgImlyaSIsCiAgICAgICAgIm5hbWUiLAogICAgICAg + ICJkZWZpbml0aW9uIiwKICAgICAgICAiaW1wb3J0ZWQiLAogICAgICAgICJv + bnRvbG9neSIsCiAgICAgICAgInN5bWJvbHMiCiAgICAgIF0KICAgIH0sCiAg + ICB7CiAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICJpcmkiOiB7ICIk + cmVmIjogImdlbmVyaWMvZGVmaW5pdGlvbnMuanNvbiMvZGVmaW5pdGlvbnMv + aXJpIiB9LAogICAgICAgICJuYW1lIjogewogICAgICAgICAgImRlc2NyaXB0 + aW9uIjogIlRoZSBuYW1lIG9mIHRoZSBzZW50ZW5jZSDigJQgdXN1YWxseSBh + dXRvLWdlbmVyYXRlZC4iLAogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgog + ICAgICAgIH0sCiAgICAgICAgImRlZmluaXRpb24iOiB7CiAgICAgICAgICAi + ZGVzY3JpcHRpb24iOiAiVGhlIGRlZmluaXRpb24gb2YgdGhlIHNlbnRlbmNl + IiwKICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICB9LAogICAg + ICAgICJpbXBvcnRlZCI6IHsKICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJE + ZW5vdGVzIHdoZXRoZXIgdGhpcyBzZW50ZW5jZSB3YXMgaW1wb3J0ZWQgZnJv + bSBhbm90aGVyIG9udG9sb2d5IiwKICAgICAgICAgICJ0eXBlIjogImJvb2xl + YW4iCiAgICAgICAgfSwKICAgICAgICAiX2xpbmtzIjogeyAiJHJlZiI6ICJn + ZW5lcmljL2RlZmluaXRpb25zLmpzb24jL2RlZmluaXRpb25zL19saW5rcyIg + fQogICAgICB9LAogICAgICAicmVxdWlyZWQiOiBbCiAgICAgICAgImlyaSIs + CiAgICAgICAgIm5hbWUiLAogICAgICAgICJkZWZpbml0aW9uIiwKICAgICAg + ICAiaW1wb3J0ZWQiLAogICAgICAgICJfbGlua3MiCiAgICAgIF0KICAgIH0K + ICBdCn0K http_version: - recorded_at: Sat, 04 Apr 2015 19:37:43 GMT + recorded_at: Wed, 13 Jan 2016 13:17:50 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -85,31 +128,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:37:43 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:3780:3BE0D8:56964E7E Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:17:51 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1133-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 6b6f4649d78d94ab396e8bd88d621787abc78db7 + Expires: + - Wed, 13 Jan 2016 13:22:51 GMT + Source-Age: + - '0' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:17:51 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:3780:3BE1EC:56964E7F + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:17:51 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1130-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - f08b3abfaa77b61e6e502349a71be6ab2f044baf + Expires: + - Wed, 13 Jan 2016 13:22:51 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -120,11 +321,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -135,11 +332,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -150,11 +343,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -165,11 +354,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -180,11 +365,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -195,11 +376,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -210,22 +387,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -236,11 +405,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -251,11 +416,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -266,11 +427,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -281,37 +438,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -319,33 +463,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:37:44 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:17:51 GMT +recorded_with: VCR 3.0.0 diff --git a/spec/fixtures/vcr/api/json-schemata/symbol.yml b/spec/fixtures/vcr/api/json-schemata/symbol.yml index 290dfdcd7..8e7339197 100644 --- a/spec/fixtures/vcr/api/json-schemata/symbol.yml +++ b/spec/fixtures/vcr/api/json-schemata/symbol.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/symbol.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/symbol.json body: encoding: US-ASCII string: '' @@ -18,82 +18,138 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:35 GMT - Content-Type: - - application/json - Content-Length: - - '1216' - Last-Modified: - - Sat, 04 Apr 2015 09:49:11 GMT - Connection: - - keep-alive - Etag: - - '"551fb397-4c0"' + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"d637562f80c27b68fb2b1295c6edb251c29bc5b7"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F1217:59C5:3C1DA5:56964E03 + Content-Length: + - '471' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:15:47 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1126-LCY + X-Cache: + - MISS + X-Cache-Hits: + - '0' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - 7bcb753a817c6e51984994a419d82dac0aa43634 + Expires: + - Wed, 13 Jan 2016 13:20:47 GMT + Source-Age: + - '0' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/symbol.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/symbol.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Symbol", "description": "A symbol defined by an ontology", "type": "object", - "properties": { - "iri": { - "description": "The unique identifier for the symbol", - "type": "string", - "format": "uri" - }, - "name": { - "description": "The name of the symbol", - "type": "string" - }, - "kind": { - "description": "The kind of symbol", - "type": "string" - }, - "definition": { - "description": "The definition of the symbol", - "type": "string" - }, - "label": { - "description": "The label that is attached to the symbol", - "type": ["string", "null"] - }, - "comment": { - "description": "A comment on the symbol", - "type": ["string", "null"] + "oneOf": [ + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the symbol", + "type": "string" + }, + "kind": { + "description": "The kind of symbol", + "type": "string" + }, + "definition": { + "description": "The definition of the symbol", + "type": "string" + }, + "label": { + "description": "The label that is attached to the symbol", + "type": ["string", "null"] + }, + "comment": { + "description": "A comment on the symbol", + "type": ["string", "null"] + }, + "ontology": { "$ref": "references.json#/definitions/ontology" }, + "sentences": { + "description": "Reference to all sentences that use the symbol in this ontology", + "$ref": "generic/definitions.json#/definitions/iri" + } + }, + "required": [ + "iri", + "name", + "kind", + "definition", + "ontology", + "sentences" + ] }, - "ontology": { "$ref": "references.json#/definitions/ontology" }, - "sentences": { - "description": "Reference to all sentences that use the symbol in this ontology", - "type": "string", - "format": "uri" + { + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the symbol", + "type": "string" + }, + "kind": { + "description": "The kind of symbol", + "type": "string" + }, + "definition": { + "description": "The definition of the symbol", + "type": "string" + }, + "label": { + "description": "The label that is attached to the symbol", + "type": ["string", "null"] + }, + "comment": { + "description": "A comment on the symbol", + "type": ["string", "null"] + }, + "_links": { "$ref": "generic/definitions.json#/definitions/_links" } + }, + "required": [ + "iri", + "name", + "kind", + "definition", + "_links" + ] } - }, - "required": [ - "iri", - "name", - "kind", - "definition", - "ontology", - "sentences" ] } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:35 GMT + recorded_at: Wed, 13 Jan 2016 13:15:47 GMT - request: method: get - uri: https://masterthesis.rightsrestricted.com/ontohub/references.json + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json body: encoding: US-ASCII string: '' @@ -109,31 +165,189 @@ http_interactions: code: 200 message: OK headers: - Server: - - nginx/1.6.2 - Date: - - Sat, 04 Apr 2015 19:38:35 GMT + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=31536000 + Etag: + - '"05f33ad940ee1376f5482f623d6f455d035ce811"' Content-Type: - - application/json + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121B:0F86:38DAC6:56964D41 Content-Length: - - '6367' - Last-Modified: - - Thu, 02 Apr 2015 10:02:57 GMT + - '947' + Accept-Ranges: + - bytes + Date: + - Wed, 13 Jan 2016 13:15:47 GMT + Via: + - 1.1 varnish Connection: - keep-alive - Etag: - - '"551d13d1-18df"' + X-Served-By: + - cache-lcy1129-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '2' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - ab58deb42c7c091a5dbcf94b87ddb9fbc610880e + Expires: + - Wed, 13 Jan 2016 13:20:47 GMT + Source-Age: + - '194' + body: + encoding: UTF-8 + string: | + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "iri": { + "description": "The IRI of a resource", + "type": "string", + "format": "iri", + "pattern": "^https?://([^.]+[.][^.]+)+/.+$" + }, + "_links": { + "description": "A _links-element corresponding to the HAL standard for hypermedia information, adopted from https://github.com/DaveJS/dave.schema.json/blob/master/hal-schema.json", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member", + "type": "object", + "properties": { + "href": { + "description": "IRI of a resource", + "type": "string", + "format": "iri" + }, + "type": { + "type": "string", + "pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$", + "format": "mime", + "description": "Hints to the media type of a target resource" + }, + "name": { + "type": "string" + }, + "hreflang": { + "type": "string", + "pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$", + "description": "Language indication of the target resource" + } + }, + "additionalProperties": false, + "required": [ + "href" + ] + }, + { + "type": "array", + "items": [ + { + "$ref": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/generic/definitions.json#hal-member" + } + ], + "uniqueItems": true, + "additionalProperties": false + } + ] + } + }, + "errors": { + "description": "Information on the specific errors that occured", + "type": "array", + "items": { + "description": "Information on one of the specific errors that occured", + "type": "string" + } + } + } + } + http_version: + recorded_at: Wed, 13 Jan 2016 13:15:47 GMT +- request: + method: get + uri: https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Security-Policy: + - default-src 'none' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - deny + X-Content-Type-Options: + - nosniff Strict-Transport-Security: - max-age=31536000 - X-Frame-Options: - - DENY + Etag: + - '"54fd49ca226b4b028f96117e95333e615b0d1f7b"' + Content-Type: + - text/plain; charset=utf-8 + Cache-Control: + - max-age=300 + X-Github-Request-Id: + - B91F121C:0F83:2628D8:56964D41 + Content-Length: + - '659' Accept-Ranges: - bytes + Date: + - Wed, 13 Jan 2016 13:15:48 GMT + Via: + - 1.1 varnish + Connection: + - keep-alive + X-Served-By: + - cache-lcy1129-LCY + X-Cache: + - HIT + X-Cache-Hits: + - '1' + Vary: + - Authorization,Accept-Encoding + Access-Control-Allow-Origin: + - "*" + X-Fastly-Request-Id: + - c1489b0a90fe08327d534e2f2e7d2dfd19d47b6b + Expires: + - Wed, 13 Jan 2016 13:20:48 GMT + Source-Age: + - '193' body: encoding: UTF-8 string: | { - "id": "https://masterthesis.rightsrestricted.com/ontohub/references.json#", + "id": "https://raw.githubusercontent.com/ontohub/ontohub-api-json/develop/references.json#", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "repository": { @@ -144,11 +358,7 @@ http_interactions: "description": "The name of the repository", "type": "string" }, - "iri": { - "description": "The unique identifier of the repository", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic": { @@ -159,11 +369,7 @@ http_interactions: "description": "The name of the logic", "type": "string" }, - "iri": { - "description": "The unique identifier of the logic", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology": { @@ -174,11 +380,7 @@ http_interactions: "description": "The name of the ontology", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "license_model": { @@ -189,11 +391,7 @@ http_interactions: "description": "The name of the license model", "type": "string" }, - "iri": { - "description": "The unique identifier of the license model", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "formality_level": { @@ -204,11 +402,7 @@ http_interactions: "description": "The name of the formality level", "type": "string" }, - "iri": { - "description": "The unique identifier of the formality level", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_type": { @@ -219,11 +413,7 @@ http_interactions: "description": "The name of the ontology type", "type": "string" }, - "iri": { - "description": "The unique identifier of the ontology type", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "ontology_version": { @@ -234,22 +424,14 @@ http_interactions: "description": "The version number of the version of the ontology", "type": "integer" }, - "iri": { - "description": "The unique identifier of the ontology-version", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "logic_mapping": { "description": "Reference to a mapping between logics", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the logic mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "mapping": { @@ -260,11 +442,7 @@ http_interactions: "description": "The name of the mapping", "type": "string" }, - "iri": { - "description": "The unique identifier of the mapping", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "sentence": { @@ -275,11 +453,7 @@ http_interactions: "description": "The name of the sentence", "type": "string" }, - "iri": { - "description": "The unique identifier of the sentence", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "symbol": { @@ -290,11 +464,7 @@ http_interactions: "description": "The name of the symbol", "type": "string" }, - "iri": { - "description": "The unique identifier of the symbol", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "theorem": { @@ -305,37 +475,24 @@ http_interactions: "description": "The name of the theorem", "type": "string" }, - "iri": { - "description": "The unique identifier of the theorem", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt": { "description": "Reference to a proof attempt", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt", - "type": "string", - "format": "uri" - }, "number": { "description": "The number of the proof attempt relative to its theorem", "type": "integer" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_status": { "description": "Reference to a proof status", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof status", - "type": "string", - "format": "uri" - }, "identifier": { "description": "The identifier of the proof status", "type": "string" @@ -343,33 +500,51 @@ http_interactions: "name": { "description": "The name of the proof status (human-readable)", "type": "string" - } + }, + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "prover_output": { "description": "Reference to a prover output", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the prover output", - "type": "string", - "format": "uri" - } + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } } }, "proof_attempt_configuration": { "description": "Reference to a proof attempt configuration", "type": "object", "properties": { - "iri": { - "description": "The unique identifier of the proof attempt configuration", - "type": "string", - "format": "uri" + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "comment": { + "description": "Reference to comment", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "review": { + "description": "Reference to a review", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" } + } + }, + "task": { + "description": "Reference to a task", + "type": "object", + "properties": { + "iri": { "$ref": "generic/definitions.json#/definitions/iri" }, + "name": { + "description": "The name of the task", + "type": "string" } } } } } http_version: - recorded_at: Sat, 04 Apr 2015 19:38:35 GMT -recorded_with: VCR 2.9.3 + recorded_at: Wed, 13 Jan 2016 13:15:48 GMT +recorded_with: VCR 3.0.0 From 2035b210b803e73defba2b0f46fe43f6f667157f Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 16:28:11 +0100 Subject: [PATCH 218/240] Style fixes. --- app/helpers/state_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/state_helper.rb b/app/helpers/state_helper.rb index d2e922b95..17171c139 100644 --- a/app/helpers/state_helper.rb +++ b/app/helpers/state_helper.rb @@ -30,7 +30,8 @@ def state_tag(resource) resource = resource.is_a?(Ontology) ? resource.current_version : resource html_opts = { - class: "evaluation-state label #{State::STATE_LABEL[resource.state.to_sym]}", + class: + "evaluation-state label #{State::STATE_LABEL[resource.state.to_sym]}", data: { klass: resource.class.to_s, id: resource.id, From 1b7a3fc7499d7703c18664d98b127f56a4bb4082 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:42:50 +0100 Subject: [PATCH 219/240] Pin bootstrap-sass version. --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c261fe81d..76fa6e894 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,8 @@ group :assets do gem 'jstree-rails', github: 'tristanm/jstree-rails' # sass-rails >= 4.0 is not compativle to rails 3 gem 'sass-rails', '~> 3.2.6' - gem 'bootstrap-sass', '~> 3.3.3' + # bootstrap-sass >= 3.3.6 is not compatible to rails 3 + gem 'bootstrap-sass', '~> 3.3.5.1' # coffee-rails > 3.2 is not compatible to rails 3 gem 'coffee-rails', '~> 3.2.2' gem 'compass', '~> 1.0.3' From f0b525b6858bf9e9eba27e47a823eda70d570a94 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:42:56 +0100 Subject: [PATCH 220/240] Bundle update. --- Gemfile.lock | 78 +++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5d14d45e4..004163e0c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,7 +61,7 @@ GEM rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.2.1) - active_model_serializers (0.9.3) + active_model_serializers (0.9.4) activemodel (>= 3.2) activemodel (3.2.22) activesupport (= 3.2.22) @@ -84,7 +84,7 @@ GEM addressable (2.3.8) ansi (1.5.0) arel (3.0.3) - autoprefixer-rails (6.1.0.1) + autoprefixer-rails (6.3.1) execjs json bcrypt (3.1.10) @@ -99,7 +99,7 @@ GEM sass (>= 3.3.0) bootstrap-select-rails (1.6.3) builder (3.0.4) - byebug (8.2.0) + byebug (8.2.1) cancan (1.6.10) capistrano (3.4.0) i18n @@ -152,7 +152,6 @@ GEM coffee-script-source execjs coffee-script-source (1.10.0) - colorize (0.7.7) compass (1.0.3) chunky_png (~> 1.2) compass-core (~> 1.0.2) @@ -166,7 +165,7 @@ GEM compass-import-once (1.0.5) sass (>= 3.2, < 3.5) connection_pool (2.2.0) - crack (0.4.2) + crack (0.4.3) safe_yaml (~> 1.0.0) cucumber (1.3.20) builder (>= 2.1.2) @@ -180,7 +179,7 @@ GEM mime-types (>= 1.16, < 3) nokogiri (~> 1.5) rails (>= 3, < 5) - d3_rails (3.5.6) + d3_rails (3.5.11) railties (>= 3.1.0) dagnabit (3.0.1) activerecord (>= 2.3.0) @@ -188,7 +187,7 @@ GEM rails (>= 3.0.0) database_cleaner (1.5.1) debug_inspector (0.0.2) - devise (3.5.2) + devise (3.5.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) @@ -204,12 +203,12 @@ GEM json thread thread_safe - elasticsearch (1.0.14) - elasticsearch-api (= 1.0.14) - elasticsearch-transport (= 1.0.14) - elasticsearch-api (1.0.14) + elasticsearch (1.0.15) + elasticsearch-api (= 1.0.15) + elasticsearch-transport (= 1.0.15) + elasticsearch-api (1.0.15) multi_json - elasticsearch-extensions (0.0.19) + elasticsearch-extensions (0.0.20) ansi ruby-prof elasticsearch-model (0.1.8) @@ -217,13 +216,13 @@ GEM elasticsearch (> 0.4) hashie elasticsearch-rails (0.1.8) - elasticsearch-transport (1.0.14) + elasticsearch-transport (1.0.15) faraday multi_json em-proxy (0.1.8) eventmachine erubis (2.7.0) - eventmachine (1.0.8) + eventmachine (1.0.9.1) exception_notification (4.1.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) @@ -297,7 +296,7 @@ GEM inherited_resources (1.4.1) has_scope (~> 0.6.0.rc) responders (~> 1.0.0.rc) - iniparse (1.4.1) + iniparse (1.4.2) invoker (1.3.2) dotenv (~> 1.0.2) em-proxy (~> 0.1) @@ -317,8 +316,8 @@ GEM jquery-ui-rails (5.0.5) railties (>= 3.2.16) json (1.8.3) - json-schema (2.5.1) - addressable (~> 2.3.7) + json-schema (2.5.2) + addressable (~> 2.3.8) json-stream (0.2.1) kaminari (0.16.3) actionpack (>= 3.0.0) @@ -346,15 +345,15 @@ GEM multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (3.0.1) + net-ssh (3.0.2) netrc (0.11.0) - nio4r (1.1.1) - nokogiri (1.6.7) + nio4r (1.2.0) + nokogiri (1.6.7.1) mini_portile2 (~> 2.0.0.rc2) options (2.3.2) orm_adapter (0.5.0) pg (0.18.4) - poltergeist (1.8.0) + poltergeist (1.8.1) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -378,7 +377,7 @@ GEM rack (1.4.7) rack-cache (1.5.1) rack (>= 0.4) - rack-mini-profiler (0.9.7) + rack-mini-profiler (0.9.8) rack (>= 1.1.3) rack-protection (1.5.3) rack @@ -394,7 +393,7 @@ GEM activesupport (= 3.2.22) bundler (~> 1.0) railties (= 3.2.22) - rails-erd (1.4.4) + rails-erd (1.4.5) activerecord (>= 3.2) activesupport (>= 3.2) choice (~> 0.2.0) @@ -407,11 +406,11 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) rainbow (2.0.0) - rake (10.4.2) - rb-fsevent (0.9.6) + rake (10.5.0) + rb-fsevent (0.9.7) rb-inotify (0.9.5) ffi (>= 0.5.0) - rdf (1.99.0) + rdf (1.99.1) link_header (~> 0.0, >= 0.0.8) rdf-aggregate-repo (1.99.0) rdf (~> 1.99) @@ -432,8 +431,8 @@ GEM rdf (~> 1.99) rdoc (3.12.2) json (~> 1.4) - redcarpet (3.3.3) - redis (3.2.1) + redcarpet (3.3.4) + redis (3.2.2) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) redis-semaphore (0.2.4) @@ -468,17 +467,17 @@ GEM rspec-expectations (~> 2.99.0) rspec-mocks (~> 2.99.0) ruby-graphviz (1.2.2) - ruby-prof (0.15.8) + ruby-prof (0.15.9) rubydns (0.8.5) eventmachine (~> 1.0.0) rugged (0.23.3) safe_yaml (1.0.4) - sass (3.4.19) + sass (3.4.21) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - secure_headers (2.4.3) + secure_headers (2.4.4) user_agent_parser shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) @@ -487,7 +486,7 @@ GEM shoulda-matchers (2.8.0) activesupport (>= 3.0.0) shoulda_routing_macros (0.1.2) - sidekiq (3.5.3) + sidekiq (3.5.4) celluloid (~> 0.17.2) connection_pool (~> 2.2, >= 2.2.0) json (~> 1.0) @@ -516,8 +515,7 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sshkit (1.7.1) - colorize (>= 0.7.0) + sshkit (1.8.1) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) state_machine (1.2.0) @@ -536,7 +534,7 @@ GEM tilt (1.4.1) timers (4.1.1) hitimes - tins (1.7.0) + tins (1.8.1) treetop (1.4.15) polyglot polyglot (>= 0.3.1) @@ -551,10 +549,10 @@ GEM user_agent_parser (2.3.0) uuid (2.3.8) macaddr (~> 1.0) - vcr (3.0.0) - warden (1.2.3) + vcr (3.0.1) + warden (1.2.4) rack (>= 1.0) - webmock (1.22.3) + webmock (1.22.6) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff @@ -574,7 +572,7 @@ DEPENDENCIES acts_as_tree (~> 2.3.0) better_errors (~> 2.1.1) binding_of_caller (~> 0.7.2) - bootstrap-sass (~> 3.3.3) + bootstrap-sass (~> 3.3.5.1) bootstrap-select-rails (~> 1.6.3) cancan (~> 1.6.7) capistrano (~> 3.4.0) @@ -663,4 +661,4 @@ DEPENDENCIES yard (~> 0.8.7.6) BUNDLED WITH - 1.10.6 + 1.11.2 From 032ff9788a304c83f9a466e8b029e5d97df178af Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:47:37 +0100 Subject: [PATCH 221/240] Update gem acts_as_tree to version 2.4.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 76fa6e894..2828ca097 100644 --- a/Gemfile +++ b/Gemfile @@ -84,7 +84,7 @@ gem 'kaminari', '~> 0.16.1' gem "strip_attributes", "~> 1.0" # For distributed ontologies -gem 'acts_as_tree', '~> 2.3.0' +gem 'acts_as_tree', '~> 2.4.0' # HTTP Client gem "rest-client", '~> 1.8.0' From 8fe476116cba2e52a42969e8ae1165a2fcbb813c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:52:08 +0100 Subject: [PATCH 222/240] Update gem rdf-rdfxml to version 1.99.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 2828ca097..1c34d2da3 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ gem 'pg', '~> 0.18.1' gem 'foreigner', '~> 1.7.2' gem 'rdf', '~> 1.99.0' -gem 'rdf-rdfxml', '~> 1.1.3' +gem 'rdf-rdfxml', '~> 1.99.0' gem 'rdf-n3', '~> 1.99.0' gem 'redis-semaphore', '~> 0.2.4' From 1974ebd587f6c126ffe11829f944d7c10c667d98 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:53:26 +0100 Subject: [PATCH 223/240] Update gem secure_headers to version 2.5.1 and migrate to 3.0 config Migration is done according to https://github.com/twitter/secureheaders/blob/4af0071ee2643e78be9bb495a1e622bb9d700a78/upgrading-to-3-0.md --- Gemfile | 2 +- config/initializers/security_headers.rb | 28 ++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Gemfile b/Gemfile index 1c34d2da3..80640f9ca 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'rails', '~> 3.2.22' gem 'rack-protection', '~> 1.5.3' -gem 'secure_headers', '~> 2.4.3' +gem 'secure_headers', '~> 2.5.1' gem 'rack-mini-profiler', require: false diff --git a/config/initializers/security_headers.rb b/config/initializers/security_headers.rb index aa4afa951..578333981 100644 --- a/config/initializers/security_headers.rb +++ b/config/initializers/security_headers.rb @@ -1,21 +1,21 @@ -::SecureHeaders::Configuration.configure do |config| - config.hsts = {:max_age => 99, :include_subdomains => true} +::SecureHeaders::Configuration.default do |config| + config.hsts = 'max_age=99; include_subdomains=true' config.x_frame_options = 'DENY' - config.x_content_type_options = "nosniff" - config.x_xss_protection = {:value => 1, :mode => false} + config.x_content_type_options = 'nosniff' + config.x_xss_protection = '1; mode=block' # By default, load resources only from own origin. # For CSS, allow styles from style elements and attributes for GWT. config.csp = { - default_src: "'self'", - style_src: "'self' 'unsafe-inline'", - script_src: "'self'", - frame_src: "'self'", - img_src: "'self'", - connect_src: "'self'", - font_src: "'self'", - media_src: "'self'", - object_src: "'self'", - child_src: "'self'", + default_src: %w('self'), + style_src: %w('self' 'unsafe-inline'), + script_src: %w('self'), + frame_src: %w('self'), + img_src: %w('self'), + connect_src: %w('self'), + font_src: %w('self'), + media_src: %w('self'), + object_src: %w('self'), + child_src: %w('self'), disable_chrome_extension: true, } end From 531a40a9aec9dcaf4b22968182eb784f92e7a571 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:55:18 +0100 Subject: [PATCH 224/240] Update gem sidekiq-status to version 0.6.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 80640f9ca..842802b2c 100644 --- a/Gemfile +++ b/Gemfile @@ -95,7 +95,7 @@ gem 'sidekiq', '~> 3.5.3' # The ref is given to ensure that no other (possibly breaking) changes are taken. gem 'sidetiq', github: 'PaulMest/sidetiq', ref: 'd88f9e483affcbadbd9e8b98b4a0a9518933887a' gem 'sidekiq-failures', '~> 0.4.5' -gem 'sidekiq-status', '~> 0.5.4' +gem 'sidekiq-status', '~> 0.6.0' gem 'sinatra', '~> 1.4.5', require: false, group: [:development, :production] # Search engine From 3143aac7fe48b57f8093145a7db8f2c2954d7506 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:56:50 +0100 Subject: [PATCH 225/240] Update gem font-awesome-sass to version 4.5.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 842802b2c..2bd98b41b 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ group :assets do # coffee-rails > 3.2 is not compatible to rails 3 gem 'coffee-rails', '~> 3.2.2' gem 'compass', '~> 1.0.3' - gem 'font-awesome-sass', '~> 4.4.0' + gem 'font-awesome-sass', '~> 4.5.0' # jquery-rails > 3.1 is not compatible to rails 3 gem 'jquery-rails', '~> 3.1.3' gem 'jquery-ui-rails', '~> 5.0.5' From 09597eaa61a1b420657c0922877d44be7cbf92f0 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:57:52 +0100 Subject: [PATCH 226/240] Update gem handlebars_assets to version 0.22.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 2bd98b41b..144d0b462 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,7 @@ group :assets do gem 'd3_rails', '~> 3.5.6' gem 'therubyracer', '~> 0.12.1' gem 'uglifier', '>= 1.0.3' - gem 'handlebars_assets', '~> 0.21.0' + gem 'handlebars_assets', '~> 0.22.0' gem 'hamlbars', '~> 2.1.1' gem 'underscore-rails', '~> 1.8.2' gem 'bootstrap-select-rails', '~> 1.6.3' From e6068e36d4e3d90e4878ec8570c7c6b7662db13e Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:58:40 +0100 Subject: [PATCH 227/240] Update gem json-schema to version 2.6.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 144d0b462..760eefc98 100644 --- a/Gemfile +++ b/Gemfile @@ -150,7 +150,7 @@ group :test do gem 'simplecov', '~> 0.10.0', require: false # So we can validate against json-schemas - gem 'json-schema', '~> 2.5.0' + gem 'json-schema', '~> 2.6.0' # Writing test ontologies gem 'ontology-united', github: '0robustus1/ontology-united' From ca8695b0a07baeb4ee748d64a2f36c2b58b0b45b Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 12:59:20 +0100 Subject: [PATCH 228/240] Update gem momentjs-rails to version 2.11.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 760eefc98..167e78c0a 100644 --- a/Gemfile +++ b/Gemfile @@ -32,7 +32,7 @@ group :assets do # jquery-rails > 3.1 is not compatible to rails 3 gem 'jquery-rails', '~> 3.1.3' gem 'jquery-ui-rails', '~> 5.0.5' - gem 'momentjs-rails', '~> 2.10.2' + gem 'momentjs-rails', '~> 2.11.0' gem 'd3_rails', '~> 3.5.6' gem 'therubyracer', '~> 0.12.1' gem 'uglifier', '>= 1.0.3' From a6071251ab8109ef9caf533569d1ff947c4d1684 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 13:01:37 +0100 Subject: [PATCH 229/240] Update gem simplecov to version 0.11.0. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 167e78c0a..c8e567346 100644 --- a/Gemfile +++ b/Gemfile @@ -147,7 +147,7 @@ group :test do gem 'cucumber-rails', '~> 1.4', require: false # Code Coverage Analysis - gem 'simplecov', '~> 0.10.0', require: false + gem 'simplecov', '~> 0.11.0', require: false # So we can validate against json-schemas gem 'json-schema', '~> 2.6.0' From f8a2e1b4ce207aab886ec1a885c7604e80597a6a Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 13:03:27 +0100 Subject: [PATCH 230/240] Bundle update. --- Gemfile.lock | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 004163e0c..1254becc5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,7 +79,7 @@ GEM multi_json (~> 1.0) acts_as_relation (0.1.3) activerecord (~> 3.2) - acts_as_tree (2.3.0) + acts_as_tree (2.4.0) activerecord (>= 3.0.0) addressable (2.3.8) ansi (1.5.0) @@ -245,7 +245,7 @@ GEM faraday (0.9.2) multipart-post (>= 1.2, < 3) ffi (1.9.10) - font-awesome-sass (4.4.0) + font-awesome-sass (4.5.0) sass (>= 3.2) foreigner (1.7.4) activerecord (>= 3.0.0) @@ -264,7 +264,7 @@ GEM haml sprockets (>= 2.0) tilt - handlebars_assets (0.21.0) + handlebars_assets (0.22.0) execjs (~> 2.0) multi_json (~> 1.0) sprockets (>= 2.0.0, < 4.0) @@ -316,7 +316,7 @@ GEM jquery-ui-rails (5.0.5) railties (>= 3.2.16) json (1.8.3) - json-schema (2.5.2) + json-schema (2.6.0) addressable (~> 2.3.8) json-stream (0.2.1) kaminari (0.16.3) @@ -338,7 +338,7 @@ GEM mini_portile2 (2.0.0) mocha (1.1.0) metaclass (~> 0.0.1) - momentjs-rails (2.10.6) + momentjs-rails (2.11.0) railties (>= 3.1) multi_json (1.11.2) multi_test (0.1.2) @@ -422,11 +422,11 @@ GEM rdf (~> 1.99) rdf-aggregate-repo (~> 1.1) rdf-xsd (~> 1.1) - rdf-rdfxml (1.1.5) + rdf-rdfxml (1.99.0) htmlentities (~> 4.3) - rdf (~> 1.1, >= 1.1.6) - rdf-rdfa (~> 1.1, >= 1.1.4.1) - rdf-xsd (~> 1.1) + rdf (~> 1.99) + rdf-rdfa (~> 1.99) + rdf-xsd (~> 1.99) rdf-xsd (1.99.0) rdf (~> 1.99) rdoc (3.12.2) @@ -477,7 +477,7 @@ GEM railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - secure_headers (2.4.4) + secure_headers (2.5.1) user_agent_parser shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) @@ -494,13 +494,13 @@ GEM redis-namespace (~> 1.5, >= 1.5.2) sidekiq-failures (0.4.5) sidekiq (>= 2.16.0) - sidekiq-status (0.5.4) + sidekiq-status (0.6.0) sidekiq (>= 2.7) sigar (0.7.3) simple_form (2.1.3) actionpack (~> 3.0) activemodel (~> 3.0) - simplecov (0.10.0) + simplecov (0.11.1) docile (~> 1.1.0) json (~> 1.8) simplecov-html (~> 0.10.0) @@ -569,7 +569,7 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.9.3) acts_as_relation (~> 0.1.3) - acts_as_tree (~> 2.3.0) + acts_as_tree (~> 2.4.0) better_errors (~> 2.1.1) binding_of_caller (~> 0.7.2) bootstrap-sass (~> 3.3.5.1) @@ -596,11 +596,11 @@ DEPENDENCIES eye (~> 0.8) factory_girl_rails (~> 4.5.0) faker (~> 1.6.1) - font-awesome-sass (~> 4.4.0) + font-awesome-sass (~> 4.5.0) foreigner (~> 1.7.2) haml-rails (~> 0.4) hamlbars (~> 2.1.1) - handlebars_assets (~> 0.21.0) + handlebars_assets (~> 0.22.0) has_scope (~> 0.6.0.rc) i18n-tasks (~> 0.8.3) indefinite_article (~> 0.2.0) @@ -608,13 +608,13 @@ DEPENDENCIES invoker (~> 1.3.2) jquery-rails (~> 3.1.3) jquery-ui-rails (~> 5.0.5) - json-schema (~> 2.5.0) + json-schema (~> 2.6.0) json-stream (~> 0.2.1) jstree-rails! kaminari (~> 0.16.1) launchy (~> 2.4.3) mocha (~> 1.1.0) - momentjs-rails (~> 2.10.2) + momentjs-rails (~> 2.11.0) nokogiri (~> 1.6.7) ontology-united! pg (~> 0.18.1) @@ -631,7 +631,7 @@ DEPENDENCIES rails_config! rdf (~> 1.99.0) rdf-n3 (~> 1.99.0) - rdf-rdfxml (~> 1.1.3) + rdf-rdfxml (~> 1.99.0) redcarpet (~> 3.3.2) redis-semaphore (~> 0.2.4) rest-client (~> 1.8.0) @@ -641,15 +641,15 @@ DEPENDENCIES ruby-graphviz (~> 1.2.2) rugged (~> 0.23.2) sass-rails (~> 3.2.6) - secure_headers (~> 2.4.3) + secure_headers (~> 2.5.1) shoulda (~> 3.5.0) shoulda_routing_macros (~> 0.1.2) sidekiq (~> 3.5.3) sidekiq-failures (~> 0.4.5) - sidekiq-status (~> 0.5.4) + sidekiq-status (~> 0.6.0) sidetiq! simple_form (~> 2.1) - simplecov (~> 0.10.0) + simplecov (~> 0.11.0) sinatra (~> 1.4.5) specroutes! strip_attributes (~> 1.0) From d4731b987a2e52862322beea492df5c4c378e06c Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 16:47:07 +0100 Subject: [PATCH 231/240] Style fix. --- app/models/ontology_member/symbol.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ontology_member/symbol.rb b/app/models/ontology_member/symbol.rb index 7c12a1878..a6827ac2c 100644 --- a/app/models/ontology_member/symbol.rb +++ b/app/models/ontology_member/symbol.rb @@ -13,7 +13,7 @@ class Symbol < ActiveRecord::Base # associations for SineAxiomSelection has_one :sine_symbol_commonness, class_name: SineSymbolCommonness, - dependent: :destroy + dependent: :destroy has_many :sine_symbol_axiom_triggers, dependent: :destroy attr_accessible :locid From 46b0d369898476f6f9709ba0f7ae2b819bd19c09 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sun, 17 Jan 2016 19:03:43 +0100 Subject: [PATCH 232/240] Fix clearing source address not changing access list. --- .../javascripts/repository/edit_remote.js.coffee | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/repository/edit_remote.js.coffee b/app/assets/javascripts/repository/edit_remote.js.coffee index 4e0280964..f3c8d533e 100644 --- a/app/assets/javascripts/repository/edit_remote.js.coffee +++ b/app/assets/javascripts/repository/edit_remote.js.coffee @@ -34,28 +34,28 @@ $ -> else set_options(options_non_mirror, to_non_mirror_option, last_value) + is_mirror_active = -> + is_mirror_selected() && is_source_address_set() + is_mirror_selected = -> remote_type_mirror_el.is(':checked') is_source_address_set = -> source_address_el.val().trim().length > 0 - was_mirror_selected = is_mirror_selected() + was_mirror_active = is_mirror_selected() was_source_address_set = is_source_address_set() - reset_mirror_selected = -> - was_mirror_selected = is_mirror_selected() - reset_was_source_address_set = -> was_source_address_set = is_source_address_set() change_options = (event) -> - if !was_mirror_selected && is_mirror_selected() + if !was_mirror_active && is_mirror_active() modify_access_list 'mirror' - else if was_mirror_selected && !is_mirror_selected() + else if was_mirror_active && !is_mirror_active() modify_access_list 'non_mirror' - else if !was_mirror_selected && !is_mirror_selected() + else if !was_mirror_active && !is_mirror_active() modify_access_list 'non_mirror' change_type_display = (event) -> @@ -73,6 +73,7 @@ $ -> source_address_el.on('input', change_type_display) source_address_el.on('input', reset_was_source_address_set) + source_address_el.on('input', change_options) _.each(remote_type_els, (el) -> $(el).on('click', change_options) ) From 33fb54f9e93483bce6f5985af497bc24218c5e88 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 23 Jan 2016 14:34:11 +0100 Subject: [PATCH 233/240] Fix double http:// in SZS ontology. --- spec/fixtures/seeds/proof_statuses.owl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/fixtures/seeds/proof_statuses.owl b/spec/fixtures/seeds/proof_statuses.owl index 0d89e184c..45afaa28a 100644 --- a/spec/fixtures/seeds/proof_statuses.owl +++ b/spec/fixtures/seeds/proof_statuses.owl @@ -6,18 +6,18 @@ - + ]> - - + From e2dd5bd92cdf48784e42150be9ef5418dd01cdd8 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Sat, 23 Jan 2016 14:34:43 +0100 Subject: [PATCH 234/240] Fix ambiguous Label location. --- app/models/proof_status/creation_from_ontology.rb | 2 +- spec/fixtures/seeds/proof_statuses.owl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/proof_status/creation_from_ontology.rb b/app/models/proof_status/creation_from_ontology.rb index 37694a6e0..fdc86fc14 100644 --- a/app/models/proof_status/creation_from_ontology.rb +++ b/app/models/proof_status/creation_from_ontology.rb @@ -34,7 +34,7 @@ def label(status) return nil unless label_sentence - match = label_sentence.text.match(/#hasLabel[^#]+#(?