diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..84aa696 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,25 @@ +on: [push, pull_request] +name: Build +jobs: + test: + name: rake test + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby-version: + - head + - "3.3" + - "3.2" + - "3.1" + - "3.0" + - "2.7" + - "2.6" + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - run: | + bundle exec rake test diff --git a/.gitignore b/.gitignore index 08761ad..b7e7725 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ -.ruby-version -.DS_Store -pkg +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +*.gem diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..851fabc --- /dev/null +++ b/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gemspec diff --git a/LICENSE b/LICENSE index 7c8c0d0..df4e409 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2014 David Graham +Copyright (c) 2010-2024 David Graham Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a1178aa..97d0757 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Again, while JSON::Stream can be used this way, if we just need to stream the document from disk or the network, we're better off using the yajl-ruby gem. Huge documents arriving over the network in small chunks to an EventMachine -receive_data loop is where JSON::Stream is really useful. Inside an +`receive_data` loop is where JSON::Stream is really useful. Inside an EventMachine::Connection subclass we might have: ```ruby @@ -52,8 +52,8 @@ def post_init end_object { puts "end object" } start_array { puts "start array" } end_array { puts "end array" } - key {|k| puts "key: #{k}" } - value {|v| puts "value: #{v}" } + key { |k| puts "key: #{k}" } + value { |v| puts "value: #{v}" } end end @@ -77,16 +77,19 @@ imagine the callbacks looking for an array named `rows` and processing sets of these row objects in small batches. Millions of rows, streaming over the network, can be processed in constant memory space this way. -## Dependencies - -* ruby >= 1.9.2 -* jruby >= 1.7 - ## Alternatives * [json](https://github.com/flori/json) * [yajl-ruby](https://github.com/brianmario/yajl-ruby) * [yajl-ffi](https://github.com/dgraham/yajl-ffi) +* [application/json-seq](http://www.rfc-editor.org/rfc/rfc7464.txt) + +## Development + +``` +$ bin/setup +$ bin/rake test +``` ## License diff --git a/bin/bundler b/bin/bundler new file mode 100755 index 0000000..d6107f0 --- /dev/null +++ b/bin/bundler @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +# +# This file was generated by Bundler. +# +# The application 'bundler' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("bundler", "bundler") diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..5864a6c --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "json/stream" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..486010f --- /dev/null +++ b/bin/rake @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +# +# This file was generated by Bundler. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rake", "rake") diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..54e9ac3 --- /dev/null +++ b/bin/setup @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install diff --git a/json-stream.gemspec b/json-stream.gemspec index 6e9131e..be96941 100644 --- a/json-stream.gemspec +++ b/json-stream.gemspec @@ -11,10 +11,12 @@ Gem::Specification.new do |s| s.homepage = 'http://dgraham.github.io/json-stream/' s.license = 'MIT' - s.files = Dir['[A-Z]*', 'json-stream.gemspec', '{lib}/**/*'] - s.test_files = Dir['spec/**/*'] + s.files = Dir['[A-Z]*', 'json-stream.gemspec', '{lib}/**/*'] - ['Gemfile.lock'] s.require_path = 'lib' - s.add_development_dependency 'rake', '~> 10.3' - s.required_ruby_version = '>= 1.9.2' + s.add_development_dependency 'bundler', '~> 2.2' + s.add_development_dependency 'minitest', '~> 5.22' + s.add_development_dependency 'rake', '~> 13.2' + + s.required_ruby_version = '>= 2.6.0' end diff --git a/lib/json/stream/buffer.rb b/lib/json/stream/buffer.rb index 4d05fe4..cbefd8b 100644 --- a/lib/json/stream/buffer.rb +++ b/lib/json/stream/buffer.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module JSON module Stream # A character buffer that expects a UTF-8 encoded stream of bytes. diff --git a/lib/json/stream/builder.rb b/lib/json/stream/builder.rb index 3ecb0f0..2ae341f 100644 --- a/lib/json/stream/builder.rb +++ b/lib/json/stream/builder.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module JSON module Stream # A parser listener that builds a full, in memory, object from a JSON diff --git a/lib/json/stream/parser.rb b/lib/json/stream/parser.rb index ca4972f..33e7925 100644 --- a/lib/json/stream/parser.rb +++ b/lib/json/stream/parser.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module JSON module Stream # Raised on any invalid JSON text. @@ -12,8 +10,8 @@ module Stream # Examples # # parser = JSON::Stream::Parser.new - # parser.key {|key| puts key } - # parser.value {|value| puts value } + # parser.key { |key| puts key } + # parser.value { |value| puts value } # parser << '{"answer":' # parser << ' 42}' class Parser @@ -85,8 +83,8 @@ def self.parse(json) # end_object { puts "end object" } # start_array { puts "start array" } # end_array { puts "end array" } - # key {|k| puts "key: #{k}" } - # value {|v| puts "value: #{v}" } + # key { |k| puts "key: #{k}" } + # value { |v| puts "value: #{v}" } # end def initialize(&block) @state = :start_document @@ -220,12 +218,12 @@ def <<(data) if @unicode.size == 4 codepoint = @unicode.slice!(0, 4).hex if codepoint >= 0xD800 && codepoint <= 0xDBFF - error('Expected low surrogate pair half') if @stack[-1].is_a?(Fixnum) + error('Expected low surrogate pair half') if @stack[-1].is_a?(Integer) @state = :start_surrogate_pair @stack.push(codepoint) elsif codepoint >= 0xDC00 && codepoint <= 0xDFFF high = @stack.pop - error('Expected high surrogate pair half') unless high.is_a?(Fixnum) + error('Expected high surrogate pair half') unless high.is_a?(Integer) pair = ((high - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000 @buf << pair @state = :start_string diff --git a/lib/json/stream/version.rb b/lib/json/stream/version.rb index 8fb44f0..b062c4d 100644 --- a/lib/json/stream/version.rb +++ b/lib/json/stream/version.rb @@ -1,7 +1,5 @@ -# encoding: UTF-8 - module JSON module Stream - VERSION = '0.2.1' + VERSION = '1.0.0' end end diff --git a/spec/buffer_spec.rb b/spec/buffer_spec.rb index 18bb122..d5c97bc 100644 --- a/spec/buffer_spec.rb +++ b/spec/buffer_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'json/stream' require 'minitest/autorun' @@ -55,39 +53,39 @@ assert_equal '[', subject << '[' assert_equal '"', subject << '"' assert_equal '', subject << "\xC3" - -> { subject << '"' }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << '"' } end it 'rejects invalid two byte start characters' do - -> { subject << "\xC3\xC3" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xC3\xC3" } end it 'rejects invalid three byte start characters' do - -> { subject << "\xE2\xE2" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xE2\xE2" } end it 'rejects invalid four byte start characters' do - -> { subject << "\xF0\xF0" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xF0\xF0" } end it 'rejects a two byte start with single byte continuation character' do - -> { subject << "\xC3\u0000" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xC3\u0000" } end it 'rejects a three byte start with single byte continuation character' do - -> { subject << "\xE2\u0010" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xE2\u0010" } end it 'rejects a four byte start with single byte continuation character' do - -> { subject << "\xF0a" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xF0a" } end it 'rejects an invalid continuation character' do - -> { subject << "\xA9" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xA9" } end it 'rejects an overlong form' do - -> { subject << "\xC0\x80" }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject << "\xC0\x80" } end describe 'checking for empty buffers' do diff --git a/spec/builder_spec.rb b/spec/builder_spec.rb index a382153..7968ce0 100644 --- a/spec/builder_spec.rb +++ b/spec/builder_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'json/stream' require 'minitest/autorun' @@ -152,6 +150,6 @@ assert_equal 'rails', subject.result['name'] assert_equal 4223, subject.result['owner']['id'] assert_equal false, subject.result['fork'] - assert_equal nil, subject.result['mirror_url'] + assert_nil subject.result['mirror_url'] end end diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index fb4547e..a273758 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'json/stream' require 'minitest/autorun' @@ -179,57 +177,57 @@ describe 'finishing the parse' do it 'rejects finish with no json data provided' do - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial null keyword' do subject << 'nul' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial true keyword' do subject << 'tru' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial false keyword' do subject << 'fals' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial float literal' do subject << '42.' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial exponent' do subject << '42e' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects malformed exponent' do subject << '42e+' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial negative number' do subject << '-' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial string literal' do subject << '"test' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial object ending in literal value' do subject << '{"test": 42' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'rejects partial array ending in literal value' do subject << '[42' - -> { subject.finish }.must_raise JSON::Stream::ParserError + assert_raises(JSON::Stream::ParserError) { subject.finish } end it 'does nothing on subsequent finish' do @@ -813,7 +811,7 @@ it 'parses a null keyword literal document' do result = JSON::Stream::Parser.parse('null') - assert_equal nil, result + assert_nil result end it 'parses a string literal document' do @@ -857,8 +855,8 @@ end_document { events << :end_document } start_object { events << :start_object } end_object { events << :end_object } - key {|k| events << [:key, k] } - value {|v| events << [:value, v] } + key { |k| events << [:key, k] } + value { |v| events << [:value, v] } end parser << '{"key":12}' expected = [:start_document, :start_object, [:key, "key"], [:value, 12], :end_object, :end_document] @@ -879,7 +877,7 @@ def events(json, parser = nil) parser ||= JSON::Stream::Parser.new collector = Events.new(parser) begin - json.each_byte {|byte| parser << [byte].pack('C') } + json.each_byte { |byte| parser << [byte].pack('C') } rescue JSON::Stream::ParserError collector.error end