diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..7a6ca3c --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,52 @@ +name: Tests + +on: + pull_request: + branches: + - master + paths-ignore: + - 'README.rdoc' + push: + branches: + - master + paths-ignore: + - 'README.rdoc' + +jobs: + unit_tests: + name: Unit Tests + # Homemade support for [ci skip] no longer needed + # https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/ + # if: "contains(github.event.commits[0].message, '[ci skip]') == false" + strategy: + fail-fast: false + matrix: + ruby: + - "3.0" + - "3.1" + - "3.2" + - "3.3" + - "jruby" + - "truffleruby" + - "truffleruby+graalvm" + allow_failures: + - false + include: + - ruby: ruby-head + allow_failures: true + - ruby: jruby-head + allow_failures: true + env: + ALLOW_FAILURES: "${{ matrix.allow_failures }}" + runs-on: ubuntu-latest + continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Test + run: bundle exec rake || $ALLOW_FAILURES diff --git a/.gitignore b/.gitignore index 37e1ae2..63a1a68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +*.gem +.bundle +Gemfile.lock *.swp -data_uri-*.gem diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3cf2eec..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: ruby -rvm: - - 1.8.7 - - jruby-18mode - - 1.9.3 - - jruby-19mode - - 2.1.0 - - rbx diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 1c03474..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,18 +0,0 @@ -PATH - remote: . - specs: - data_uri (0.0.3) - -GEM - remote: https://rubygems.org/ - specs: - minitest (5.2.3) - rake (10.1.1) - -PLATFORMS - ruby - -DEPENDENCIES - data_uri! - minitest - rake diff --git a/data_uri.gemspec b/data_uri.gemspec index c2435ab..cb17a9f 100644 --- a/data_uri.gemspec +++ b/data_uri.gemspec @@ -7,13 +7,16 @@ Gem::Specification.new do |s| s.description = "URI class for parsing data URIs" s.summary = "A URI class for parsing data URIs as per RFC2397" + s.required_ruby_version = ">= 3.0" + s.platform = Gem::Platform::RUBY - s.has_rdoc = true s.extra_rdoc_files = ["README.rdoc"] s.require_path = 'lib' s.files = %w(README.rdoc Rakefile) + Dir.glob("lib/**/*") + s.add_dependency "base64" + s.add_development_dependency 'rake' s.add_development_dependency 'minitest' end diff --git a/lib/data_uri.rb b/lib/data_uri.rb index 62fa940..320fc2b 100644 --- a/lib/data_uri.rb +++ b/lib/data_uri.rb @@ -1,5 +1,3 @@ -require 'uri' -require 'base64' -require 'stringio' +# frozen_string_literal: true require 'data_uri/uri' diff --git a/lib/data_uri/open_uri.rb b/lib/data_uri/open_uri.rb deleted file mode 100644 index b485c79..0000000 --- a/lib/data_uri/open_uri.rb +++ /dev/null @@ -1,22 +0,0 @@ -module URI - - class Data - - def open - io = StringIO.new(data) - OpenURI::Meta.init(io) - io.meta_add_field('content-type', content_type) - if block_given? - begin - yield io - ensure - io.close - end - else - io - end - end - - end - -end diff --git a/lib/data_uri/uri.rb b/lib/data_uri/uri.rb index 47cfa9e..9b02180 100644 --- a/lib/data_uri/uri.rb +++ b/lib/data_uri/uri.rb @@ -1,3 +1,8 @@ +# frozen_string_literal: true + +require 'uri' +require 'base64' + module URI class Data < Generic @@ -20,25 +25,25 @@ def initialize(*args) super(*args) end @data = @opaque - if md = MIME_TYPE_RE.match(@data) + if (md = MIME_TYPE_RE.match(@data)) @content_type = md[1] @data = @data[@content_type.length .. -1] end @content_type ||= 'text/plain' @mime_params = {} - while md = MIME_PARAM_RE.match(@data) + while (md = MIME_PARAM_RE.match(@data)) @mime_params[md[1]] = md[2] @data = @data[md[0].length .. -1] end - if base64 = /^;base64/.match(@data) + if (base64 = /^;base64/.match(@data)) @data = @data[7 .. -1] end unless /^,/.match(@data) raise URI::InvalidURIError.new('Invalid data URI') end @data = @data[1 .. -1] - @data = base64 ? Base64.decode64(@data) : URI.decode(@data) - if @data.respond_to?(:force_encoding) && charset = @mime_params['charset'] + @data = base64 ? Base64.decode64(@data) : URI.decode_www_form_component(@data) + if @data.respond_to?(:force_encoding) && (charset = @mime_params['charset']) @data.force_encoding(charset) end end @@ -61,6 +66,11 @@ def self.build(arg) end end - @@schemes['DATA'] = Data + unless methods(false).include?(:register_scheme) + def self.register_scheme(scheme, klass) + @@schemes[scheme] = klass + end + end + register_scheme('DATA', Data) end diff --git a/test/test_data_open_uri.rb b/test/test_data_open_uri.rb deleted file mode 100644 index dab79f0..0000000 --- a/test/test_data_open_uri.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'data_uri' -require 'open-uri' -require 'data_uri/open_uri' -require 'minitest/autorun' -require 'minitest/spec' - -describe URI::Data do - - describe "a valid data URI" do - - before do - @base64 = "R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw==" - @uri = URI.parse("data:image/gif;base64,#{@base64}") - @data = Base64.decode64(@base64) - end - - it "should open" do - @uri.open.read.must_equal @data - end - - it "should open with a block" do - @uri.open do |io| - io.read.must_equal @data - end - end - - it "should have content_type on opened IO" do - @uri.open.content_type.must_equal 'image/gif' - end - - it "should open on Kernel.open" do - open(@uri).read.must_equal @uri.data - end - - end - -end diff --git a/test/test_data_uri.rb b/test/test_data_uri.rb index 84c2c08..6406ba8 100644 --- a/test/test_data_uri.rb +++ b/test/test_data_uri.rb @@ -5,71 +5,71 @@ describe URI::Data do describe "parsing" do - + describe "a base64 encoded image/gif data URI" do - + before do @base64 = "R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw==" @uri = URI.parse("data:image/gif;base64,#{@base64}") end - + it "should parse as a URI::Data object" do - @uri.class.must_equal URI::Data + _(@uri.class).must_equal URI::Data end - + it "should have a content_type of image/gif" do - @uri.content_type.must_equal 'image/gif' + _(@uri.content_type).must_equal 'image/gif' end - + it "should have data" do require 'base64' - @uri.data.must_equal Base64.decode64(@base64) + _(@uri.data).must_equal Base64.decode64(@base64) end - + end - + describe "a text/plain data URI" do - + before do @uri = URI.parse("data:,A%20brief%20note") end - + it "should parse as a URI::Data object" do - @uri.class.must_equal URI::Data + _(@uri.class).must_equal URI::Data end - + it "should have a content_type of text/plain" do - @uri.content_type.must_equal 'text/plain' + _(@uri.content_type).must_equal 'text/plain' end - + it "should have data" do - @uri.data.must_equal 'A brief note' + _(@uri.data).must_equal 'A brief note' end - + end - + describe "a text/html data URI with a charset" do - + before do @uri = URI.parse("data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%3E%0D%0A%3Chead%3E%3Ctitle%3EEmbedded%20Window%3C%2Ftitle%3E%3C%2Fhead%3E%0D%0A%3Cbody%3E%3Ch1%3E42%3C%2Fh1%3E%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A%0D%0A") end - + it "should parse as a URI::Data object" do - @uri.class.must_equal URI::Data + _(@uri.class).must_equal URI::Data end - + it "should have a content_type of text/html" do - @uri.content_type.must_equal 'text/html' + _(@uri.content_type).must_equal 'text/html' end - + it "should have data" do - @uri.data.must_equal "\r\n\r\nEmbedded Window\r\n

42

\n\n\r\n" + _(@uri.data).must_equal "\r\n\r\nEmbedded Window\r\n

42

\n\n\r\n" end - + end describe "a big data binary data URI" do - + before do @data = Array.new(100000) { rand(256) }.pack('c*') @raw = "data:application/octet-stream;base64,#{Base64.encode64(@data).chop}" @@ -80,7 +80,7 @@ uri = URI.parse(@raw) refute_equal uri.data, @data else - proc { URI.parse(@raw) }.must_raise(URI::InvalidURIError) + _(proc { URI.parse(@raw) }).must_raise(URI::InvalidURIError) end end @@ -93,8 +93,8 @@ describe "an invalid data URI" do it "should raise an error" do - proc { URI::Data.new("This is not a data URI") }.must_raise(URI::InvalidURIError) - proc { URI::Data.new("data:Neither this") }.must_raise(URI::InvalidURIError) + _(proc { URI::Data.new("This is not a data URI") }).must_raise(URI::InvalidURIError) + _(proc { URI::Data.new("data:Neither this") }).must_raise(URI::InvalidURIError) end end @@ -108,7 +108,7 @@ it "given data and an explicit content_type" do uri = URI::Data.build(:content_type => 'image/gif', :data => StringIO.new(@data)) - uri.to_s.must_equal 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==' + _(uri.to_s).must_equal 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==' end it "given data with an implicit content_type" do @@ -116,13 +116,13 @@ (class << io; self; end).instance_eval { attr_accessor :content_type } io.content_type = 'image/gif' uri = URI::Data.build(:data => io) - uri.to_s.must_equal 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==' + _(uri.to_s).must_equal 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==' end it "given data and no content_type" do io = StringIO.new("foobar") uri = URI::Data.build(:data => io) - uri.to_s.must_equal 'data:;base64,Zm9vYmFy' + _(uri.to_s).must_equal 'data:;base64,Zm9vYmFy' end end