From a08362390381c43cca011052f67853f9c3530917 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 29 Mar 2022 13:11:10 -0700 Subject: [PATCH 1/7] Add `:uri` option to `Format,content_type`, and `uris` and `to_uri` class methods. This allows retrieving the defined format URI for specified formats. (See https://www.w3.org/ns/formats/). --- lib/rdf/format.rb | 36 +++++++++++++++++++++++++++++++++-- lib/rdf/nquads.rb | 5 ++++- lib/rdf/ntriples/format.rb | 5 ++++- spec/format_spec.rb | 39 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/lib/rdf/format.rb b/lib/rdf/format.rb index b96c5277..ade3f64e 100644 --- a/lib/rdf/format.rb +++ b/lib/rdf/format.rb @@ -23,7 +23,9 @@ module RDF # # @example Defining a new RDF serialization format class # class RDF::NTriples::Format < RDF::Format - # content_type 'application/n-triples', extension: :nt + # content_type 'application/n-triples', + # extension: :nt, + # uri: RDF::URI("http://www.w3.org/ns/formats/N-Triples") # content_encoding 'utf-8' # # reader RDF::NTriples::Reader @@ -222,6 +224,19 @@ def self.file_extensions @@file_extensions end + ## + # Returns the unique URI for the format. + # + # @example retrieving a list of supported file format URIs + # + # RDF::Format.uris.keys + # + # @see https://www.w3.org/ns/formats/ + # @return [Hash{Symbol => URI}] + def self.uris + @@uris + end + ## # Returns the set of format symbols for available RDF::Reader subclasses. # @@ -465,6 +480,7 @@ class << self # @option options [Array] :aliases (nil) # @option options [Symbol] :extension (nil) # @option options [Array] :extensions (nil) + # @option options [URI] :uri (nil) # @return [void] # # @overload content_type @@ -504,6 +520,10 @@ def self.content_type(type = nil, options = {}) @@content_types[aa] << self unless @@content_types[aa].include?(self) end end + # URI identifying this format + if uri = options[:uri] + @@uris[RDF::URI(uri)] = self + end end end @@ -517,7 +537,7 @@ def self.accept_type end ## - # Retrieves or defines file extensions for this RDF serialization format. + # Retrieves file extensions for this RDF serialization format. # # The return is an array where the first element is the cannonical # file extension for the format and following elements are alias file extensions. @@ -527,6 +547,17 @@ def self.file_extension @@file_extensions.map {|ext, formats| ext if formats.include?(self)}.compact end + ## + # Retrieves any format URI defined for this format.. + # + # @return [URI] + def self.uri + @@uris.invert[self] + end + class << self + alias_method :to_uri, :uri + end + protected ## @@ -568,6 +599,7 @@ def self.content_encoding(encoding = nil) @@readers = {} # @private @@writers = {} # @private @@subclasses = [] # @private + @@uris = {} # @private ## # @private diff --git a/lib/rdf/nquads.rb b/lib/rdf/nquads.rb index 875cf295..dc142716 100644 --- a/lib/rdf/nquads.rb +++ b/lib/rdf/nquads.rb @@ -20,7 +20,10 @@ module NQuads # @see http://www.w3.org/TR/n-quads/ # @since 0.4.0 class Format < RDF::Format - content_type 'application/n-quads', extension: :nq, alias: 'text/x-nquads;q=0.2' + content_type 'application/n-quads', + extension: :nq, + alias: 'text/x-nquads;q=0.2', + uri: RDF::URI("http://www.w3.org/ns/formats/N-Quads") content_encoding 'utf-8' reader { RDF::NQuads::Reader } diff --git a/lib/rdf/ntriples/format.rb b/lib/rdf/ntriples/format.rb index 8d570e8e..afafab8a 100644 --- a/lib/rdf/ntriples/format.rb +++ b/lib/rdf/ntriples/format.rb @@ -16,7 +16,10 @@ module RDF::NTriples # @see http://www.w3.org/TR/rdf-testcases/#ntriples # @see http://www.w3.org/TR/n-triples/ class Format < RDF::Format - content_type 'application/n-triples', extension: :nt, alias: 'text/plain;q=0.2' + content_type 'application/n-triples', + extension: :nt, + alias: 'text/plain;q=0.2', + uri: RDF::URI("http://www.w3.org/ns/formats/N-Triples") content_encoding 'utf-8' reader { RDF::NTriples::Reader } diff --git a/spec/format_spec.rb b/spec/format_spec.rb index 934d96c5..cb1bcfc0 100644 --- a/spec/format_spec.rb +++ b/spec/format_spec.rb @@ -5,7 +5,8 @@ class RDF::Format::FooFormat < RDF::Format content_type 'application/test;q=1', - extension: :test + extension: :test, + uri: "http://example.com/ns/formats/Foo" reader { RDF::NTriples::Reader } def self.detect(sample) sample == "foo" @@ -16,7 +17,8 @@ def self.to_sym; :foo_bar; end class RDF::Format::BarFormat < RDF::Format content_type 'application/test', extension: :test, - alias: 'application/x-test;q=0.1' + alias: 'application/x-test;q=0.1', + uri: "http://example.com/ns/formats/Bar" reader { RDF::NTriples::Reader } def self.detect(sample) sample == "bar" @@ -129,6 +131,16 @@ def self.to_sym; :foo_bar; end end end + describe ".uris" do + it "returns accept-types of available readers with quality" do + expect(RDF::Format.accept_types).to include(*%w( + application/n-triples text/plain;q=0.2 + application/n-quads text/x-nquads;q=0.2 + application/test application/x-test;q=0.1 + )) + end + end + describe ".writer_symbols" do it "returns symbols of available writers" do %i(ntriples nquads).each do |sym| @@ -152,6 +164,15 @@ def self.to_sym; :foo_bar; end end end + describe ".uris" do + it "matches format classes to URIs" do + RDF::Format.uris.each do |u, c| + expect(u).to be_a(RDF::URI) + expect(c).to respond_to(:content_type) + end + end + end + RDF::Format.each do |format| context format.name do subject {format} @@ -166,6 +187,20 @@ def self.to_sym; :foo_bar; end end end + describe RDF::NTriples::Format do + it "has a format URI" do + expect(described_class.uri).to eql RDF::URI('http://www.w3.org/ns/formats/N-Triples') + expect(described_class.to_uri).to eql RDF::URI('http://www.w3.org/ns/formats/N-Triples') + end + end + + describe RDF::NQuads::Format do + it "has a format URI" do + expect(described_class.uri).to eql RDF::URI('http://www.w3.org/ns/formats/N-Quads') + expect(described_class.to_uri).to eql RDF::URI('http://www.w3.org/ns/formats/N-Quads') + end + end + context "Examples" do require 'rdf/ntriples' subject {RDF::Format} From fc5380a3a92b42eafcad2063089e36bdb9518114 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 20 Apr 2022 16:50:35 -0700 Subject: [PATCH 2/7] pass `format` keyword parameter as option when filtering CLI commands. --- lib/rdf/cli.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rdf/cli.rb b/lib/rdf/cli.rb index defaf7d8..6efb2d6b 100644 --- a/lib/rdf/cli.rb +++ b/lib/rdf/cli.rb @@ -572,7 +572,8 @@ def self.exec(args, output: $stdout, option_parser: nil, messages: {}, **options # @param [Hash{Symbol => Object}] options already set # @return [Array] list of executable commands # @overload commands(format: :json, **options) - # @param [:json] format (:json) + # Returns commands as JSON, for API usage. + # @param [:json] format # @param [Hash{Symbol => Object}] options already set # @return [Array{Object}] # Returns an array of commands including the command symbol @@ -593,7 +594,7 @@ def self.commands(format: nil, **options) # Subset commands based on filter options cmds = COMMANDS.reject do |k, c| c.fetch(:filter, {}).any? do |opt, val| - options[opt].to_s != val.to_s + options.merge(format: format)[opt].to_s != val.to_s end end From f82fce1fb3db67bc15577b0eb8e2d8137a4a4633 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 20 Apr 2022 16:51:04 -0700 Subject: [PATCH 3/7] Check for windows drive letter to not confuse it with a scheme. --- lib/rdf/model/uri.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rdf/model/uri.rb b/lib/rdf/model/uri.rb index 07695b68..e6a3f320 100644 --- a/lib/rdf/model/uri.rb +++ b/lib/rdf/model/uri.rb @@ -76,6 +76,7 @@ class URI IRI = Regexp.compile("^#{SCHEME}:(?:#{IHIER_PART})(?:\\?#{IQUERY})?(?:\\##{IFRAGMENT})?$").freeze # Split an IRI into it's component parts + # scheme, authority, path, query, fragment IRI_PARTS = /^(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(\?[^#]*)?(#.*)?$/.freeze # Remove dot expressions regular expressions @@ -872,6 +873,12 @@ def parse(value) parts = {} if matchdata = IRI_PARTS.match(value) scheme, authority, path, query, fragment = matchdata[1..-1] + + if Gem.win_platform? && scheme.match?(/^[a-zA-Z]$/) && authority.to_s.empty? + # A drive letter, not a scheme + scheme, path = nil, "#{scheme}:#{path}" + end + userinfo, hostport = authority.to_s.split('@', 2) hostport, userinfo = userinfo, nil unless hostport user, password = userinfo.to_s.split(':', 2) From 600581402bbecdf47ba6ed00e6d85fcda24e7099 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 20 Apr 2022 16:51:42 -0700 Subject: [PATCH 4/7] CI on windows. --- .github/workflows/ci.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7343175f..3c439cb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,3 +48,31 @@ jobs: if: "matrix.ruby == '3.0'" with: github-token: ${{ secrets.GITHUB_TOKEN }} + wintests: + name: Win64 Ruby ${{ matrix.ruby }} + if: "contains(github.event.commits[0].message, '[ci skip]') == false" + runs-on: windows-latest + env: + CI: true + ALLOW_FAILURES: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'jruby' }} + strategy: + fail-fast: false + matrix: + ruby: + - 3.1 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + - name: Run tests + run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES + - name: Coveralls GitHub Action + uses: coverallsapp/github-action@v1.1.2 + if: "matrix.ruby == '3.0'" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 0b218f2553ef18f0d0fac57c6fd0a1792d7c54d4 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 20 Apr 2022 17:03:30 -0700 Subject: [PATCH 5/7] Tweak scheme comparison. --- lib/rdf/model/uri.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rdf/model/uri.rb b/lib/rdf/model/uri.rb index e6a3f320..9de45e68 100644 --- a/lib/rdf/model/uri.rb +++ b/lib/rdf/model/uri.rb @@ -874,7 +874,7 @@ def parse(value) if matchdata = IRI_PARTS.match(value) scheme, authority, path, query, fragment = matchdata[1..-1] - if Gem.win_platform? && scheme.match?(/^[a-zA-Z]$/) && authority.to_s.empty? + if Gem.win_platform? && scheme && !authority && scheme.match?(/^[a-zA-Z]$/) # A drive letter, not a scheme scheme, path = nil, "#{scheme}:#{path}" end From 532e3cdcb7e11f52c2574d3b66dd9208ce4ea468 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Thu, 21 Apr 2022 13:43:28 -0700 Subject: [PATCH 6/7] Spec tweaks for running on windows. --- spec/model_uri_spec.rb | 2 +- spec/ntriples_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/model_uri_spec.rb b/spec/model_uri_spec.rb index 7ac6b9f1..76fd1eed 100644 --- a/spec/model_uri_spec.rb +++ b/spec/model_uri_spec.rb @@ -620,7 +620,7 @@ %w(http://foo/bar# /a) => "", %w(http://foo/bar# #a) => "", - %w(http://a/bb/ccc/.. g:h) => "", + %w(http://a/bb/ccc/.. gg:h) => "", %w(http://a/bb/ccc/.. g) => "", %w(http://a/bb/ccc/.. ./g) => "", %w(http://a/bb/ccc/.. g/) => "", diff --git a/spec/ntriples_spec.rb b/spec/ntriples_spec.rb index 774a4329..c71e601c 100644 --- a/spec/ntriples_spec.rb +++ b/spec/ntriples_spec.rb @@ -630,7 +630,7 @@ expect(output.string).to eq "#{stmt_string}\n" end - it "should dump statements to a file" do + it "should dump statements to a file", skip: ('not windows' if Gem.win_platform?) do require 'tmpdir' # for Dir.tmpdir writer.dump(graph, filename = File.join(Dir.tmpdir, "test.nt")) expect(File.read(filename)).to eq "#{stmt_string}\n" From 857389590af8b4455b3df865207aa7aa75838a41 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Fri, 22 Apr 2022 15:52:04 -0700 Subject: [PATCH 7/7] Version 3.2.7. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 34cde569..406ebcbd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.6 +3.2.7