Hello community, here is the log from the commit of package rubygem-nio4r for openSUSE:Factory checked in at 2018-02-12 10:13:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-nio4r (Old) and /work/SRC/openSUSE:Factory/.rubygem-nio4r.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-nio4r" Mon Feb 12 10:13:33 2018 rev:4 rq:574047 version:2.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-nio4r/rubygem-nio4r.changes 2017-06-02 10:33:50.735553748 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-nio4r.new/rubygem-nio4r.changes 2018-02-12 10:13:35.513014684 +0100 @@ -1,0 +2,44 @@ +Tue Jan 9 07:37:26 UTC 2018 - co...@suse.com + +- updated to version 2.2.0 + see installed CHANGES.md + + ## 2.2.0 (2017-12-27) + + * [#151](https://github.com/socketry/nio4r/pull/151) + `NIO::Selector`: Support for enumerating and configuring backend + ([@tarcieri]) + + * [#153](https://github.com/socketry/nio4r/pull/153) + Fix builds on Windows + ([@unak]) + + * [#157](https://github.com/socketry/nio4r/pull/157) + Windows / MinGW test failure - fix spec_helper.rb + ([@MSP-Greg]) + + * [#162](https://github.com/socketry/nio4r/pull/162) + Don't build the C extension on Windows + ([@larskanis]) + + * [#164](https://github.com/socketry/nio4r/pull/164) + Fix NIO::ByteBuffer leak + ([@HoneyryderChuck]) + + * [#170](https://github.com/socketry/nio4r/pull/170) + Avoid CancelledKeyExceptions on JRuby + ([@HoneyryderChuck]) + + * [#177](https://github.com/socketry/nio4r/pull/177) + Fix `NIO::ByteBuffer` string conversions on JRuby + ([@tarcieri]) + + * [#179](https://github.com/socketry/nio4r/pull/179) + Fix argument error when running on ruby 2.5.0 + ([@tompng]) + + * [#180](https://github.com/socketry/nio4r/pull/180) + ext/nio4r/extconf.rb: check for port_event_t in port.h (fixes #178) + ([@tarcieri]) + +------------------------------------------------------------------- Old: ---- nio4r-2.1.0.gem New: ---- nio4r-2.2.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-nio4r.spec ++++++ --- /var/tmp/diff_new_pack.qLKd60/_old 2018-02-12 10:13:36.720971153 +0100 +++ /var/tmp/diff_new_pack.qLKd60/_new 2018-02-12 10:13:36.724971009 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-nio4r # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ # Name: rubygem-nio4r -Version: 2.1.0 +Version: 2.2.0 Release: 0 %define mod_name nio4r %define mod_full_name %{mod_name}-%{version} @@ -33,7 +33,7 @@ BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 Url: https://github.com/socketry/nio4r -Source: http://rubygems.org/gems/%{mod_full_name}.gem +Source: https://rubygems.org/gems/%{mod_full_name}.gem Source1: rubygem-nio4r-rpmlintrc Source2: gem2rpm.yml Summary: New IO for Ruby ++++++ nio4r-2.1.0.gem -> nio4r-2.2.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.rubocop.yml new/.rubocop.yml --- old/.rubocop.yml 2017-05-28 22:58:36.000000000 +0200 +++ new/.rubocop.yml 2017-12-27 17:33:22.000000000 +0100 @@ -1,4 +1,5 @@ AllCops: + TargetRubyVersion: 2.2 DisplayCopNames: true # @@ -18,6 +19,9 @@ Metrics/AbcSize: Max: 35 +Metrics/BlockLength: + Max: 128 + Metrics/ClassLength: Max: 128 @@ -35,11 +39,21 @@ Max: 15 # +# Performance +# + +Performance/RegexpMatch: + Enabled: false + +# # Style # -Style/StringLiterals: - EnforcedStyle: double_quotes +Style/FormatStringToken: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: true Style/GlobalVars: Enabled: false @@ -50,5 +64,11 @@ Style/RescueModifier: Enabled: false +Style/SafeNavigation: + Enabled: false + +Style/StringLiterals: + EnforcedStyle: double_quotes + Style/TrivialAccessors: Enabled: false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.ruby-version new/.ruby-version --- old/.ruby-version 2017-05-28 22:58:36.000000000 +0200 +++ new/.ruby-version 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -2.4.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.travis.yml new/.travis.yml --- old/.travis.yml 2017-05-28 22:58:36.000000000 +0200 +++ new/.travis.yml 2017-12-27 17:33:22.000000000 +0100 @@ -3,7 +3,7 @@ cache: bundler before_install: - - gem update --system 2.6.10 + - gem update --system - gem --version bundler_args: --without development @@ -13,10 +13,11 @@ - master rvm: - - jruby-9.1.10.0 # latest stable + - jruby-9.1.15.0 # latest stable - 2.2.7 - 2.3.4 - 2.4.1 + - 2.5.0 - ruby-head env: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGES.md new/CHANGES.md --- old/CHANGES.md 2017-05-28 22:58:36.000000000 +0200 +++ new/CHANGES.md 2017-12-27 17:33:22.000000000 +0100 @@ -1,3 +1,41 @@ +## 2.2.0 (2017-12-27) + +* [#151](https://github.com/socketry/nio4r/pull/151) + `NIO::Selector`: Support for enumerating and configuring backend + ([@tarcieri]) + +* [#153](https://github.com/socketry/nio4r/pull/153) + Fix builds on Windows + ([@unak]) + +* [#157](https://github.com/socketry/nio4r/pull/157) + Windows / MinGW test failure - fix spec_helper.rb + ([@MSP-Greg]) + +* [#162](https://github.com/socketry/nio4r/pull/162) + Don't build the C extension on Windows + ([@larskanis]) + +* [#164](https://github.com/socketry/nio4r/pull/164) + Fix NIO::ByteBuffer leak + ([@HoneyryderChuck]) + +* [#170](https://github.com/socketry/nio4r/pull/170) + Avoid CancelledKeyExceptions on JRuby + ([@HoneyryderChuck]) + +* [#177](https://github.com/socketry/nio4r/pull/177) + Fix `NIO::ByteBuffer` string conversions on JRuby + ([@tarcieri]) + +* [#179](https://github.com/socketry/nio4r/pull/179) + Fix argument error when running on ruby 2.5.0 + ([@tompng]) + +* [#180](https://github.com/socketry/nio4r/pull/180) + ext/nio4r/extconf.rb: check for port_event_t in port.h (fixes #178) + ([@tarcieri]) + ## 2.1.0 (2017-05-28) * [#130](https://github.com/socketry/nio4r/pull/130) @@ -163,3 +201,8 @@ [@johnnyt]: https://github.com/johnnyt [@UpeksheJay]: https://github.com/UpeksheJay [@junaruga]: https://github.com/junaruga +[@unak]: https://github.com/unak +[@MSP-Greg]: https://github.com/MSP-Greg +[@larskanis]: https://github.com/larskanis +[@HoneyryderChuck]: https://github.com/HoneyryderChuck +[@tompng]: https://github.com/tompng diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Gemfile new/Gemfile --- old/Gemfile 2017-05-28 22:58:36.000000000 +0200 +++ new/Gemfile 2017-12-27 17:33:22.000000000 +0100 @@ -14,7 +14,7 @@ group :development, :test do gem "coveralls", require: false gem "rake-compiler", require: false - gem "rspec", "~> 3", require: false + gem "rspec", "~> 3.7", require: false gem "rspec-retry", require: false - gem "rubocop", "0.46.0", require: false + gem "rubocop", "0.52.1", require: false end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Guardfile new/Guardfile --- old/Guardfile 2017-05-28 22:58:36.000000000 +0200 +++ new/Guardfile 2017-12-27 17:33:22.000000000 +0100 @@ -1,6 +1,6 @@ # frozen_string_literal: true -directories %w(lib spec) +directories %w[lib spec] clearing :on guard :rspec, cmd: "bundle exec rspec" do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2017-05-28 22:58:36.000000000 +0200 +++ new/README.md 2017-12-27 17:33:22.000000000 +0100 @@ -1,9 +1,11 @@ # ![nio4r](https://raw.github.com/socketry/nio4r/master/logo.png) [![Gem Version](https://badge.fury.io/rb/nio4r.svg)](http://rubygems.org/gems/nio4r) -[![Build Status](https://secure.travis-ci.org/socketry/nio4r.svg?branch=master)](http://travis-ci.org/socketry/nio4r) +[![Travis CI Status](https://secure.travis-ci.org/socketry/nio4r.svg?branch=master)](http://travis-ci.org/socketry/nio4r) +[![Appveyor Status](https://ci.appveyor.com/api/projects/status/1ru8x81v91vaewax/branch/master?svg=true)](https://ci.appveyor.com/project/tarcieri/nio4r/branch/master) [![Code Climate](https://codeclimate.com/github/socketry/nio4r.svg)](https://codeclimate.com/github/socketry/nio4r) [![Coverage Status](https://coveralls.io/repos/socketry/nio4r/badge.svg?branch=master)](https://coveralls.io/r/socketry/nio4r) +[![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/nio4r/2.2.0) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/socketry/nio4r/blob/master/LICENSE.txt) _NOTE: This is the 2.x **stable** branch of nio4r. For the 1.x **legacy** branch, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Rakefile new/Rakefile --- old/Rakefile 2017-05-28 22:58:36.000000000 +0200 +++ new/Rakefile 2017-12-27 17:33:22.000000000 +0100 @@ -5,6 +5,6 @@ Dir[File.expand_path("../tasks/**/*.rake", __FILE__)].each { |task| load task } -task default: %w(compile spec rubocop) +task default: %w[compile spec rubocop] CLEAN.include "**/*.o", "**/*.so", "**/*.bundle", "**/*.jar", "pkg", "tmp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/appveyor.yml new/appveyor.yml --- old/appveyor.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/appveyor.yml 2017-12-27 17:33:22.000000000 +0100 @@ -0,0 +1,27 @@ +branches: + only: + - master + +environment: + PATH: C:\Ruby%RUBY_VERSION%\DevKit\mingw\bin;C:\Ruby%RUBY_VERSION%\bin;C:\Ruby%RUBY_VERSION%\DevKit\bin;%PATH% + matrix: + - RUBY_VERSION: "24-x64" + - RUBY_VERSION: "23-x64" + - RUBY_VERSION: "23" + - RUBY_VERSION: "22" + +install: + - SET RAKEOPT=-rdevkit + - ruby -v + - gem -v + - bundle -v + - bundle install + +build: off + +before_build: + - gem update --system + +test_script: + - echo %PATH% + - bundle exec rake spec Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/bytebuffer.c new/ext/nio4r/bytebuffer.c --- old/ext/nio4r/bytebuffer.c 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/bytebuffer.c 2017-12-27 17:33:22.000000000 +0100 @@ -75,6 +75,7 @@ static VALUE NIO_ByteBuffer_allocate(VALUE klass) { struct NIO_ByteBuffer *bytebuffer = (struct NIO_ByteBuffer *)xmalloc(sizeof(struct NIO_ByteBuffer)); + bytebuffer->buffer = NULL; return Data_Wrap_Struct(klass, NIO_ByteBuffer_gc_mark, NIO_ByteBuffer_free, bytebuffer); } @@ -84,6 +85,8 @@ static void NIO_ByteBuffer_free(struct NIO_ByteBuffer *buffer) { + if(buffer->buffer) + xfree(buffer->buffer); xfree(buffer); } @@ -124,10 +127,11 @@ static VALUE NIO_ByteBuffer_set_position(VALUE self, VALUE new_position) { + int pos; struct NIO_ByteBuffer *buffer; Data_Get_Struct(self, struct NIO_ByteBuffer, buffer); - int pos = NUM2INT(new_position); + pos = NUM2INT(new_position); if(pos < 0) { rb_raise(rb_eArgError, "negative position given"); @@ -156,10 +160,11 @@ static VALUE NIO_ByteBuffer_set_limit(VALUE self, VALUE new_limit) { + int lim; struct NIO_ByteBuffer *buffer; Data_Get_Struct(self, struct NIO_ByteBuffer, buffer); - int lim = NUM2INT(new_limit); + lim = NUM2INT(new_limit); if(lim < 0) { rb_raise(rb_eArgError, "negative limit given"); @@ -237,10 +242,11 @@ static VALUE NIO_ByteBuffer_fetch(VALUE self, VALUE index) { + int i; struct NIO_ByteBuffer *buffer; Data_Get_Struct(self, struct NIO_ByteBuffer, buffer); - int i = NUM2INT(index); + i = NUM2INT(index); if(i < 0) { rb_raise(rb_eArgError, "negative index given"); @@ -255,10 +261,12 @@ static VALUE NIO_ByteBuffer_put(VALUE self, VALUE string) { + long length; struct NIO_ByteBuffer *buffer; Data_Get_Struct(self, struct NIO_ByteBuffer, buffer); - long length = RSTRING_LEN(string); + StringValue(string); + length = RSTRING_LEN(string); if(length > buffer->limit - buffer->position) { rb_raise(cNIO_ByteBuffer_OverflowError, "buffer is full"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/extconf.rb new/ext/nio4r/extconf.rb --- old/ext/nio4r/extconf.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/extconf.rb 2017-12-27 17:33:22.000000000 +0100 @@ -1,35 +1,26 @@ # frozen_string_literal: true -require "mkmf" - -have_header("unistd.h") - -$defs << "-DEV_USE_SELECT" if have_header("sys/select.h") +require "rubygems" -$defs << "-DEV_USE_POLL" if have_header("poll.h") - -$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h") - -if have_header("sys/event.h") && have_header("sys/queue.h") - $defs << "-DEV_USE_KQUEUE" +# Write a dummy Makefile on Windows because we use the pure Ruby implementation there +if Gem.win_platform? + File.write("Makefile", "all install::\n") + File.write("nio4r_ext.so", "") + exit end -$defs << "-DEV_USE_PORT" if have_header("port.h") +require "mkmf" +have_header("unistd.h") + +$defs << "-DEV_USE_SELECT" if have_header("sys/select.h") +$defs << "-DEV_USE_POLL" if have_type("port_event_t", "poll.h") +$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h") +$defs << "-DEV_USE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h") +$defs << "-DEV_USE_PORT" if have_type("port_event_t", "port.h") $defs << "-DHAVE_SYS_RESOURCE_H" if have_header("sys/resource.h") -CONFIG["optflags"] << " -fno-strict-aliasing" +CONFIG["optflags"] << " -fno-strict-aliasing" unless RUBY_PLATFORM =~ /mswin/ dir_config "nio4r_ext" create_makefile "nio4r_ext" - -# win32 needs to link in "just the right order" for some reason or -# ioctlsocket will be mapped to an [inverted] ruby specific version. -if RUBY_PLATFORM =~ /mingw|win32/ - makefile_contents = File.read "Makefile" - - makefile_contents.gsub! "DLDFLAGS = ", "DLDFLAGS = -export-all " - - makefile_contents.gsub! "LIBS = $(LIBRUBYARG_SHARED)", "LIBS = -lws2_32 $(LIBRUBYARG_SHARED)" - File.open("Makefile", "w") { |f| f.write makefile_contents } -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/monitor.c new/ext/nio4r/monitor.c --- old/ext/nio4r/monitor.c 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/monitor.c 2017-12-27 17:33:22.000000000 +0100 @@ -92,7 +92,7 @@ monitor->interests = EV_READ | EV_WRITE; } else { rb_raise(rb_eArgError, "invalid event type %s (must be :r, :w, or :rw)", - RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0, 0))); + RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0))); } GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr); @@ -258,7 +258,7 @@ return EV_READ | EV_WRITE; } else { rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)", - RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0, 0))); + RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0))); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/nio4r_ext.c new/ext/nio4r/nio4r_ext.c --- old/ext/nio4r/nio4r_ext.c 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/nio4r_ext.c 2017-12-27 17:33:22.000000000 +0100 @@ -12,6 +12,8 @@ void Init_nio4r_ext() { + ev_set_allocator(xrealloc); + Init_NIO_Selector(); Init_NIO_Monitor(); Init_NIO_ByteBuffer(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/org/nio4r/ByteBuffer.java new/ext/nio4r/org/nio4r/ByteBuffer.java --- old/ext/nio4r/org/nio4r/ByteBuffer.java 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/org/nio4r/ByteBuffer.java 2017-12-27 17:33:22.000000000 +0100 @@ -163,10 +163,8 @@ @JRubyMethod(name = "<<") public IRubyObject put(ThreadContext context, IRubyObject str) { - String string = str.asJavaString(); - try { - this.byteBuffer.put(string.getBytes()); + this.byteBuffer.put(str.convertToString().getByteList().bytes()); } catch(BufferOverflowException e) { throw ByteBuffer.newOverflowError(context, "buffer is full"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/org/nio4r/Monitor.java new/ext/nio4r/org/nio4r/Monitor.java --- old/ext/nio4r/org/nio4r/Monitor.java 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/org/nio4r/Monitor.java 2017-12-27 17:33:22.000000000 +0100 @@ -102,12 +102,16 @@ @JRubyMethod public IRubyObject readiness(ThreadContext context) { + if(!key.isValid()) + return this.interests; return Nio4r.interestOpsToSymbol(context.getRuntime(), key.readyOps()); } @JRubyMethod(name = "readable?") public IRubyObject isReadable(ThreadContext context) { Ruby runtime = context.getRuntime(); + if (!this.key.isValid()) + return runtime.getTrue(); int readyOps = this.key.readyOps(); if((readyOps & SelectionKey.OP_READ) != 0 || (readyOps & SelectionKey.OP_ACCEPT) != 0) { @@ -120,6 +124,8 @@ @JRubyMethod(name = {"writable?", "writeable?"}) public IRubyObject writable(ThreadContext context) { Ruby runtime = context.getRuntime(); + if (!this.key.isValid()) + return runtime.getTrue(); int readyOps = this.key.readyOps(); if((readyOps & SelectionKey.OP_WRITE) != 0 || (readyOps & SelectionKey.OP_CONNECT) != 0) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/org/nio4r/Selector.java new/ext/nio4r/org/nio4r/Selector.java --- old/ext/nio4r/org/nio4r/Selector.java 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/org/nio4r/Selector.java 2017-12-27 17:33:22.000000000 +0100 @@ -7,6 +7,7 @@ import java.nio.channels.Channel; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; +import java.nio.channels.CancelledKeyException; import org.jruby.Ruby; import org.jruby.RubyArray; @@ -30,8 +31,23 @@ super(ruby, rubyClass); } + @JRubyMethod(meta = true) + public static IRubyObject backends(ThreadContext context, IRubyObject self) { + return context.runtime.newArray(context.runtime.newSymbol("java")); + } + @JRubyMethod public IRubyObject initialize(ThreadContext context) { + initialize(context, context.runtime.newSymbol("java")); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject backend) { + if(backend != context.runtime.newSymbol("java")) { + throw context.runtime.newArgumentError(":java is the only supported backend"); + } + this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>(); this.wakeupFired = false; @@ -193,6 +209,7 @@ while(selectedKeys.hasNext()) { SelectionKey key = (SelectionKey)selectedKeys.next(); processKey(key); + selectedKeys.remove(); if(block.isGiven()) { @@ -254,7 +271,7 @@ // Remove connect interest from connected sockets // See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why private void processKey(SelectionKey key) { - if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) { + if(key.isValid() && (key.readyOps() & SelectionKey.OP_CONNECT) != 0) { int interestOps = key.interestOps(); interestOps &= ~SelectionKey.OP_CONNECT; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/nio4r/selector.c new/ext/nio4r/selector.c --- old/ext/nio4r/selector.c 2017-05-28 22:58:36.000000000 +0200 +++ new/ext/nio4r/selector.c 2017-12-27 17:33:22.000000000 +0100 @@ -27,8 +27,11 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector); static void NIO_Selector_free(struct NIO_Selector *loop); -/* Methods */ -static VALUE NIO_Selector_initialize(VALUE self); +/* Class methods */ +static VALUE NIO_Selector_supported_backends(VALUE klass); + +/* Instance methods */ +static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self); static VALUE NIO_Selector_backend(VALUE self); static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest); static VALUE NIO_Selector_deregister(VALUE self, VALUE io); @@ -65,7 +68,8 @@ cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject); rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate); - rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, 0); + rb_define_singleton_method(cNIO_Selector, "backends", NIO_Selector_supported_backends, 0); + rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, -1); rb_define_method(cNIO_Selector, "backend", NIO_Selector_backend, 0); rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2); rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1); @@ -102,7 +106,9 @@ } selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector)); - selector->ev_loop = ev_loop_new(0); + + /* Defer initializing the loop to #initialize */ + selector->ev_loop = 0; ev_init(&selector->timer, NIO_Selector_timeout_callback); @@ -112,8 +118,6 @@ ev_io_init(&selector->wakeup, NIO_Selector_wakeup_callback, selector->wakeup_reader, EV_READ); selector->wakeup.data = (void *)selector; - ev_io_start(selector->ev_loop, &selector->wakeup); - selector->closed = selector->selecting = selector->wakeup_fired = selector->ready_count = 0; selector->ready_array = Qnil; @@ -154,12 +158,83 @@ xfree(selector); } +/* Return an array of symbols for supported backends */ +static VALUE NIO_Selector_supported_backends(VALUE klass) { + unsigned int backends = ev_supported_backends(); + VALUE result = rb_ary_new(); + + if(backends & EVBACKEND_EPOLL) { + rb_ary_push(result, ID2SYM(rb_intern("epoll"))); + } + + if(backends & EVBACKEND_POLL) { + rb_ary_push(result, ID2SYM(rb_intern("poll"))); + } + + if(backends & EVBACKEND_KQUEUE) { + rb_ary_push(result, ID2SYM(rb_intern("kqueue"))); + } + + if(backends & EVBACKEND_SELECT) { + rb_ary_push(result, ID2SYM(rb_intern("select"))); + } + + if(backends & EVBACKEND_PORT) { + rb_ary_push(result, ID2SYM(rb_intern("port"))); + } + + return result; +} + /* Create a new selector. This is more or less the pure Ruby version translated into an MRI cext */ -static VALUE NIO_Selector_initialize(VALUE self) +static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self) { + ID backend_id; + VALUE backend; VALUE lock; + struct NIO_Selector *selector; + unsigned int flags = 0; + + Data_Get_Struct(self, struct NIO_Selector, selector); + + rb_scan_args(argc, argv, "01", &backend); + + if(backend != Qnil) { + if(!rb_ary_includes(NIO_Selector_supported_backends(CLASS_OF(self)), backend)) { + rb_raise(rb_eArgError, "unsupported backend: %s", + RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0))); + } + + backend_id = SYM2ID(backend); + + if(backend_id == rb_intern("epoll")) { + flags = EVBACKEND_EPOLL; + } else if(backend_id == rb_intern("poll")) { + flags = EVBACKEND_POLL; + } else if(backend_id == rb_intern("kqueue")) { + flags = EVBACKEND_KQUEUE; + } else if(backend_id == rb_intern("select")) { + flags = EVBACKEND_SELECT; + } else if(backend_id == rb_intern("port")) { + flags = EVBACKEND_PORT; + } else { + rb_raise(rb_eArgError, "unsupported backend: %s", + RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0))); + } + } + + /* Ensure the selector loop has not yet been initialized */ + assert(!selector->ev_loop); + + selector->ev_loop = ev_loop_new(flags); + if(!selector->ev_loop) { + rb_raise(rb_eIOError, "error initializing event loop"); + } + + ev_io_start(selector->ev_loop, &selector->wakeup); + rb_ivar_set(self, rb_intern("selectables"), rb_hash_new()); rb_ivar_set(self, rb_intern("lock_holder"), Qnil); @@ -204,7 +279,7 @@ if(lock_holder != current_thread) { lock = rb_ivar_get(self, rb_intern("lock")); - rb_funcall(lock, rb_intern("lock"), 0, 0); + rb_funcall(lock, rb_intern("lock"), 0); rb_ivar_set(self, rb_intern("lock_holder"), current_thread); /* We've acquired the lock, so ensure we unlock it */ @@ -223,7 +298,7 @@ rb_ivar_set(self, rb_intern("lock_holder"), Qnil); lock = rb_ivar_get(self, rb_intern("lock")); - rb_funcall(lock, rb_intern("unlock"), 0, 0); + rb_funcall(lock, rb_intern("unlock"), 0); return Qnil; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/nio/bytebuffer.rb new/lib/nio/bytebuffer.rb --- old/lib/nio/bytebuffer.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/lib/nio/bytebuffer.rb 2017-12-27 17:33:22.000000000 +0100 @@ -48,10 +48,8 @@ raise ArgumentError, "negative position given" if new_position < 0 raise ArgumentError, "specified position exceeds capacity" if new_position > @capacity + @mark = nil if @mark && @mark > new_position @position = new_position - @mark = nil if @mark && @mark > @position - - new_position end # Set the limit to the given value. New limit must be less than capacity. @@ -65,11 +63,9 @@ raise ArgumentError, "negative limit given" if new_limit < 0 raise ArgumentError, "specified limit exceeds capacity" if new_limit > @capacity + @position = new_limit if @position > new_limit + @mark = nil if @mark && @mark > new_limit @limit = new_limit - @position = new_limit if @position > @limit - @mark = nil if @mark && @mark > @limit - - new_limit end # Number of bytes remaining in the buffer before the limit @@ -115,15 +111,22 @@ # Add a String to the buffer # + # @param str [#to_str] data to add to the buffer + # + # @raise [TypeError] given a non-string type # @raise [NIO::ByteBuffer::OverflowError] buffer is full # # @return [self] - def <<(str) + def put(str) + raise TypeError, "expected String, got #{str.class}" unless str.respond_to?(:to_str) + str = str.to_str + raise OverflowError, "buffer is full" if str.length > @limit - @position @buffer[@position...str.length] = str @position += str.length self end + alias << put # Perform a non-blocking read from the given IO object into the buffer # Reads as much data as is immediately available and returns diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/nio/monitor.rb new/lib/nio/monitor.rb --- old/lib/nio/monitor.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/lib/nio/monitor.rb 2017-12-27 17:33:22.000000000 +0100 @@ -31,7 +31,7 @@ # @return [Symbol] new interests def interests=(interests) raise EOFError, "monitor is closed" if closed? - raise ArgumentError, "bad interests: #{interests}" unless [:r, :w, :rw].include?(interests) + raise ArgumentError, "bad interests: #{interests}" unless %i[r w rw].include?(interests) @interests = interests end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/nio/selector.rb new/lib/nio/selector.rb --- old/lib/nio/selector.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/lib/nio/selector.rb 2017-12-27 17:33:22.000000000 +0100 @@ -5,8 +5,17 @@ module NIO # Selectors monitor IO objects for events of interest class Selector + # Return supported backends as symbols + # + # See `#backend` method definition for all possible backends + def self.backends + [:ruby] + end + # Create a new NIO::Selector - def initialize + def initialize(backend = :ruby) + raise ArgumentError, "unsupported backend: #{backend}" unless backend == :ruby + @selectables = {} @lock = Mutex.new diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/nio/version.rb new/lib/nio/version.rb --- old/lib/nio/version.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/lib/nio/version.rb 2017-12-27 17:33:22.000000000 +0100 @@ -1,5 +1,5 @@ # frozen_string_literal: true module NIO - VERSION = "2.1.0" + VERSION = "2.2.0".freeze end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/nio.rb new/lib/nio.rb --- old/lib/nio.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/lib/nio.rb 2017-12-27 17:33:22.000000000 +0100 @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "thread" require "socket" require "nio/version" @@ -19,7 +18,7 @@ require "nio/monitor" require "nio/selector" require "nio/bytebuffer" - NIO::ENGINE = "ruby" + NIO::ENGINE = "ruby".freeze else require "nio4r_ext" @@ -27,8 +26,8 @@ require "java" require "jruby" org.nio4r.Nio4r.new.load(JRuby.runtime, false) - NIO::ENGINE = "java" + NIO::ENGINE = "java".freeze else - NIO::ENGINE = "libev" + NIO::ENGINE = "libev".freeze end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2017-05-28 22:58:36.000000000 +0200 +++ new/metadata 2017-12-27 17:33:22.000000000 +0100 @@ -1,17 +1,17 @@ --- !ruby/object:Gem::Specification name: nio4r version: !ruby/object:Gem::Version - version: 2.1.0 + version: 2.2.0 platform: ruby authors: - Tony Arcieri autorequire: bindir: bin cert_chain: [] -date: 2017-05-28 00:00:00.000000000 Z +date: 2017-12-27 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency - name: rake + name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ">=" @@ -25,7 +25,7 @@ - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency - name: bundler + name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" @@ -50,7 +50,6 @@ - ".gitignore" - ".rspec" - ".rubocop.yml" -- ".ruby-version" - ".travis.yml" - CHANGES.md - Gemfile @@ -58,6 +57,7 @@ - LICENSE.txt - README.md - Rakefile +- appveyor.yml - examples/echo_server.rb - ext/libev/Changes - ext/libev/LICENSE @@ -125,7 +125,7 @@ version: '0' requirements: [] rubyforge_project: -rubygems_version: 2.6.11 +rubygems_version: 2.7.3 signing_key: specification_version: 4 summary: New IO for Ruby diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nio4r.gemspec new/nio4r.gemspec --- old/nio4r.gemspec 2017-05-28 22:58:36.000000000 +0200 +++ new/nio4r.gemspec 2017-12-27 17:33:22.000000000 +0100 @@ -29,6 +29,6 @@ spec.extensions = ["ext/nio4r/extconf.rb"] end - spec.add_development_dependency "rake" spec.add_development_dependency "bundler" + spec.add_development_dependency "rake" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/acceptables_spec.rb new/spec/nio/acceptables_spec.rb --- old/spec/nio/acceptables_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/acceptables_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -20,13 +20,13 @@ let(:port) { next_available_tcp_port } let :acceptable_subject do - server = TCPServer.new("localhost", port) - TCPSocket.open("localhost", port) + server = TCPServer.new("127.0.0.1", port) + TCPSocket.open("127.0.0.1", port) server end let :unacceptable_subject do - TCPServer.new("localhost", port + 1) + TCPServer.new("127.0.0.1", port + 1) end it_behaves_like "an NIO acceptable" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/bytebuffer_spec.rb new/spec/nio/bytebuffer_spec.rb --- old/spec/nio/bytebuffer_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/bytebuffer_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -2,6 +2,7 @@ require "spec_helper" +# rubocop:disable Metrics/BlockLength RSpec.describe NIO::ByteBuffer do let(:capacity) { 256 } let(:example_string) { "Testing 1 2 3..." } @@ -179,6 +180,11 @@ expect(bytebuffer.limit).to eq capacity end + it "raises TypeError if given a non-String type" do + expect { bytebuffer << 42 }.to raise_error(TypeError) + expect { bytebuffer << nil }.to raise_error(TypeError) + end + it "raises NIO::ByteBuffer::OverflowError if the buffer is full" do bytebuffer << "X" * (capacity - 1) expect { bytebuffer << "X" }.not_to raise_error @@ -281,7 +287,7 @@ end context "I/O" do - let(:addr) { "localhost" } + let(:addr) { "127.0.0.1" } let(:port) { next_available_tcp_port } let(:server) { TCPServer.new(addr, port) } let(:client) { TCPSocket.new(addr, port) } @@ -347,3 +353,4 @@ end end end +# rubocop:enable Metrics/BlockLength diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/monitor_spec.rb new/spec/nio/monitor_spec.rb --- old/spec/nio/monitor_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/monitor_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -4,7 +4,7 @@ require "socket" RSpec.describe NIO::Monitor do - let(:addr) { "localhost" } + let(:addr) { "127.0.0.1" } let(:port) { next_available_tcp_port } let(:reader) { TCPServer.new(addr, port) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/selectables/pipe_spec.rb new/spec/nio/selectables/pipe_spec.rb --- old/spec/nio/selectables/pipe_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/selectables/pipe_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -27,12 +27,17 @@ # will throw EAGAIN if there is too little space to write the string # TODO: Use FFI to lookup the platform-specific size of PIPE_BUF str = "JUNK IN THE TUBES" * 10_000 + cntr = 0 begin pipe.write_nonblock str - _, writers = select [], [pipe], [], 0 + cntr += 1 + t = select [], [pipe], [], 0 rescue Errno::EPIPE break - end while writers && writers.include?(pipe) + rescue IO::EWOULDBLOCKWaitWritable + skip "windows - can't test due to 'select' not showing correct status" + break + end while t && t[1].include?(pipe) && cntr < 20 pipe end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/selectables/ssl_socket_spec.rb new/spec/nio/selectables/ssl_socket_spec.rb --- old/spec/nio/selectables/ssl_socket_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/selectables/ssl_socket_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -4,23 +4,23 @@ require "openssl" RSpec.describe OpenSSL::SSL::SSLSocket do - let(:addr) { "localhost" } + let(:addr) { "127.0.0.1" } let(:port) { next_available_tcp_port } let(:ssl_key) { OpenSSL::PKey::RSA.new(1024) } let(:ssl_cert) do - name = OpenSSL::X509::Name.new([%w(CN localhost)]) + name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]]) OpenSSL::X509::Certificate.new.tap do |cert| cert.version = 2 cert.serial = 1 cert.issuer = name cert.subject = name cert.not_before = Time.now - cert.not_after = Time.now + (365 * 24 * 60 * 60) + cert.not_after = Time.now + (7 * 24 * 60 * 60) cert.public_key = ssl_key.public_key - cert.sign(ssl_key, OpenSSL::Digest::SHA1.new) + cert.sign(ssl_key, OpenSSL::Digest::SHA256.new) end end @@ -111,14 +111,15 @@ ssl_peer.accept thread.join + cntr = 0 begin - _, writers = select [], [ssl_client], [], 0 count = ssl_client.write_nonblock "X" * 1024 expect(count).not_to eq(0) + cntr += 1 + t = select [], [ssl_client], [], 0 rescue IO::WaitReadable, IO::WaitWritable pending "SSL will report writable but not accept writes" - raise if writers.include? ssl_client - end while writers && writers.include?(ssl_client) + end while t && t[1].include?(ssl_client) && cntr < 30 # I think the kernel might manage to drain its buffer a bit even after # the socket first goes unwritable. Attempt to sleep past this and then @@ -141,8 +142,6 @@ end let :pair do - pending "figure out why newly created sockets are selecting readable immediately" - server = TCPServer.new(addr, port) client = TCPSocket.new(addr, port) peer = server.accept diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/selectables/tcp_socket_spec.rb new/spec/nio/selectables/tcp_socket_spec.rb --- old/spec/nio/selectables/tcp_socket_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/selectables/tcp_socket_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -19,9 +19,7 @@ sock = TCPSocket.new(addr, port) # Sanity check to make sure we actually produced an unreadable socket - if select([sock], [], [], 0) - pending "Failed to produce an unreadable socket" - end + pending "Failed to produce an unreadable socket" if select([sock], [], [], 0) sock end @@ -57,9 +55,7 @@ end # Sanity check to make sure we actually produced an unwritable socket - if select([], [sock], [], 0) - pending "Failed to produce an unwritable socket" - end + pending "Failed to produce an unwritable socket" if select([], [sock], [], 0) sock end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/selectables/udp_socket_spec.rb new/spec/nio/selectables/udp_socket_spec.rb --- old/spec/nio/selectables/udp_socket_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/selectables/udp_socket_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -2,27 +2,37 @@ require "spec_helper" -RSpec.describe UDPSocket do +RSpec.describe UDPSocket, if: !defined?(JRUBY_VERSION) do let(:udp_port) { 23_456 } let :readable_subject do sock = UDPSocket.new - sock.bind("localhost", udp_port) + sock.bind("127.0.0.1", udp_port) peer = UDPSocket.new - peer.send("hi there", 0, "localhost", udp_port) + peer.send("hi there", 0, "127.0.0.1", udp_port) sock end let :unreadable_subject do sock = UDPSocket.new - sock.bind("localhost", udp_port + 1) + sock.bind("127.0.0.1", udp_port + 1) sock end let :writable_subject do - pending "come up with a writable UDPSocket example" + peer = UDPSocket.new + peer.connect "127.0.0.1", udp_port + cntr = 0 + begin + peer.send("X" * 1024, 0) + cntr += 1 + t = select [], [peer], [], 0 + rescue Errno::ECONNREFUSED => ex + skip "Couln't make writable UDPSocket subject: #{ex.class}: #{ex}" + end while t && t[1].include?(peer) && cntr < 5 + peer end let :unwritable_subject do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/nio/selector_spec.rb new/spec/nio/selector_spec.rb --- old/spec/nio/selector_spec.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/nio/selector_spec.rb 2017-12-27 17:33:22.000000000 +0100 @@ -8,11 +8,35 @@ # the tests TIMEOUT_PRECISION = 0.1 +# rubocop:disable Metrics/BlockLength RSpec.describe NIO::Selector do let(:pair) { IO.pipe } let(:reader) { pair.first } let(:writer) { pair.last } + context ".backends" do + it "knows all supported backends" do + expect(described_class.backends).to be_a Array + expect(described_class.backends.first).to be_a Symbol + end + end + + context "#initialize" do + it "allows explicitly specifying a backend" do + backend = described_class.backends.first + selector = described_class.new(backend) + expect(selector.backend).to eq backend + end + + it "raises ArgumentError if given an invalid backend" do + expect { described_class.new(:derp) }.to raise_error ArgumentError + end + + it "raises TypeError if given a non-Symbol parameter" do + expect { described_class.new(42).to raise_error TypeError } + end + end + context "backend" do it "knows its backend" do expect(subject.backend).to be_a Symbol @@ -191,3 +215,4 @@ expect(subject).to be_closed end end +# rubocop:enable Metrics/BlockLength diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/spec_helper.rb new/spec/spec_helper.rb --- old/spec/spec_helper.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/spec_helper.rb 2017-12-27 17:33:22.000000000 +0100 @@ -20,8 +20,8 @@ $current_tcp_port += 1 begin - sock = Timeout.timeout(1) { TCPSocket.new("localhost", $current_tcp_port) } - rescue Errno::ECONNREFUSED + sock = Timeout.timeout(0.5) { TCPSocket.new("127.0.0.1", $current_tcp_port) } + rescue Errno::ECONNREFUSED, Timeout::Error break $current_tcp_port end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/support/selectable_examples.rb new/spec/support/selectable_examples.rb --- old/spec/support/selectable_examples.rb 2017-05-28 22:58:36.000000000 +0200 +++ new/spec/support/selectable_examples.rb 2017-12-27 17:33:22.000000000 +0100 @@ -34,9 +34,6 @@ let(:peer) { pair.last } it "selects readable when the other end closes" do - # hax: this test is broken for OpenSSL sockets - skip "broken for SSL ;_;" if peer.is_a? OpenSSL::SSL::SSLSocket - monitor = selector.register(stream, :r) expect(selector.select(0)).to be_nil @@ -57,4 +54,12 @@ expect(m.readiness).to eq(:rw) end end + it "keeps readiness after the selectable has been closed" do + selector.register(readable_subject, :rw) + selector.select(0) do |m| + expect(m.readiness).to eq(:rw) + readable_subject.close + expect(m.readiness).to eq(:rw) + end + end end