summaryrefslogtreecommitdiff
path: root/spec/ruby/library/rexml/text/empty_spec.rb
AgeCommit message (Expand)Author
2018-03-04Update to ruby/spec@c1b568beregon
2017-09-20Move spec/rubyspec to spec/ruby for consistencyeregon
yml?h=v3_2_9&id2=085790bdc0936aec793f6798f9b78c10916c8292'>.appveyor.yml27
-rw-r--r--.cirrus.yml64
-rw-r--r--.document3
-rw-r--r--.github/CODEOWNERS11
-rw-r--r--.github/dependabot.yml2
-rw-r--r--.github/workflows/baseruby.yml26
-rw-r--r--.github/workflows/bundled_gems.yml41
-rw-r--r--.github/workflows/check_dependencies.yml23
-rw-r--r--.github/workflows/check_misc.yml99
-rw-r--r--.github/workflows/codeql-analysis.yml50
-rw-r--r--.github/workflows/compilers.yml71
-rw-r--r--.github/workflows/macos.yml29
-rw-r--r--.github/workflows/mingw.yml36
-rw-r--r--.github/workflows/mjit-bindgen.yml104
-rw-r--r--.github/workflows/mjit.yml46
-rw-r--r--.github/workflows/publish.yml18
-rw-r--r--.github/workflows/scorecards.yml72
-rw-r--r--.github/workflows/spec_guards.yml52
-rw-r--r--.github/workflows/ubuntu.yml49
-rw-r--r--.github/workflows/wasm.yml45
-rw-r--r--.github/workflows/windows.yml83
-rw-r--r--.github/workflows/yjit-ubuntu.yml64
-rw-r--r--.gitignore13
-rw-r--r--.travis.yml234
-rw-r--r--LEGAL10
-rw-r--r--NEWS.md743
-rw-r--r--README.ja.md1
-rw-r--r--README.md5
-rw-r--r--addr2line.c430
-rw-r--r--array.c90
-rw-r--r--ast.c60
-rw-r--r--ast.rb110
-rw-r--r--benchmark/array_sort_int.yml15
-rw-r--r--benchmark/buffer_each.yml27
-rw-r--r--benchmark/buffer_get.yml25
-rw-r--r--benchmark/cgi_escape_html.yml37
-rw-r--r--benchmark/enum_minmax.yml25
-rw-r--r--benchmark/enum_sort.yml15
-rw-r--r--benchmark/erb_escape_html.yml31
-rw-r--r--benchmark/lib/benchmark_driver/runner/mjit.rb2
-rw-r--r--benchmark/numeric_methods.yml16
-rw-r--r--benchmark/range_min.yml2
-rw-r--r--benchmark/string_concat.yml10
-rw-r--r--benchmark/time_parse.yml2
-rw-r--r--benchmark/vm_const.yml6
-rw-r--r--benchmark/vm_freezeobj.yml6
-rw-r--r--benchmark/vm_ivar_get.yml37
-rw-r--r--benchmark/vm_ivar_get_unintialized.yml12
-rw-r--r--benchmark/vm_ivar_lazy_set.yml12
-rw-r--r--benchmark/vm_lvar_cond_set.yml8
-rw-r--r--bignum.c10
-rwxr-xr-xbootstraptest/runner.rb94
-rw-r--r--bootstraptest/test_attr.rb16
-rw-r--r--bootstraptest/test_io.rb1
-rw-r--r--bootstraptest/test_ractor.rb56
-rw-r--r--bootstraptest/test_yjit.rb466
-rw-r--r--builtin.h4
-rw-r--r--ccan/check_type/check_type.h2
-rw-r--r--ccan/container_of/container_of.h4
-rw-r--r--ccan/list/list.h2
-rw-r--r--class.c173
-rw-r--r--common.mk1184
-rw-r--r--compar.c2
-rw-r--r--compile.c588
-rw-r--r--complex.c160
-rw-r--r--configure.ac423
-rw-r--r--cont.c903
-rw-r--r--coroutine/asyncify/Context.h8
-rw-r--r--coroutine/ppc/Context.S117
-rw-r--r--coroutine/ppc/Context.h1
-rw-r--r--coroutine/ppc64/Context.S121
-rw-r--r--coroutine/ppc64/Context.h4
-rw-r--r--cygwin/GNUmakefile.in34
-rw-r--r--debug.c1
-rw-r--r--debug_counter.c20
-rw-r--r--debug_counter.h45
-rw-r--r--defs/gmake.mk72
-rw-r--r--defs/id.def6
-rw-r--r--defs/keywords2
-rw-r--r--defs/lex.c.src2
-rw-r--r--dir.c6
-rw-r--r--dln.c14
-rw-r--r--doc/.document1
-rw-r--r--doc/ChangeLog-2.3.010
-rw-r--r--doc/NEWS/NEWS-3.0.0.md12
-rw-r--r--doc/command_injection.rdoc2
-rw-r--r--doc/contributing/building_ruby.md50
-rw-r--r--doc/contributing/documentation_guide.md35
-rw-r--r--doc/encodings.rdoc11
-rw-r--r--doc/examples/files.rdoc26
-rw-r--r--doc/mjit/mjit.md39
-rw-r--r--doc/net-http/examples.rdoc31
-rw-r--r--doc/net-http/included_getters.rdoc3
-rw-r--r--doc/packed_data.rdoc590
-rw-r--r--doc/regexp.rdoc20
-rw-r--r--doc/syntax/literals.rdoc6
-rw-r--r--doc/time/in.rdoc4
-rw-r--r--doc/time/mon-min.rdoc8
-rw-r--r--doc/time/msec.rdoc2
-rw-r--r--doc/time/nsec.rdoc2
-rw-r--r--doc/time/sec.rdoc2
-rw-r--r--doc/time/sec_i.rdoc1
-rw-r--r--doc/time/usec.rdoc2
-rw-r--r--doc/time/year.rdoc1
-rw-r--r--doc/time/zone_and_in.rdoc5
-rw-r--r--doc/timezone_specifiers.rdoc46
-rw-r--r--doc/timezones.rdoc108
-rw-r--r--doc/yjit/yjit.md236
-rw-r--r--enc/Makefile.in2
-rw-r--r--enc/cesu_8.c23
-rw-r--r--enc/depend1
-rw-r--r--enc/encdb.c2
-rw-r--r--enc/jis/props.h.blt4
-rw-r--r--enc/jis/props.kwd2
-rw-r--r--enc/jis/props.src2
-rwxr-xr-xenc/make_encmake.rb2
-rw-r--r--enc/unicode/15.0.0/casefold.h (renamed from enc/unicode/14.0.0/casefold.h)8
-rw-r--r--enc/unicode/15.0.0/name2ctype.h (renamed from enc/unicode/14.0.0/name2ctype.h)3097
-rw-r--r--enc/utf_16_32.h2
-rw-r--r--encoding.c180
-rw-r--r--enum.c113
-rw-r--r--enumerator.c194
-rw-r--r--error.c130
-rw-r--r--eval.c157
-rw-r--r--eval_error.c112
-rw-r--r--eval_jump.c2
-rw-r--r--ext/-test-/marshal/internal_ivar/internal_ivar.c5
-rw-r--r--ext/-test-/num2int/num2int.c26
-rw-r--r--ext/-test-/random/bad_version.c135
-rw-r--r--ext/-test-/random/depend160
-rw-r--r--ext/-test-/random/loop.c1
-rw-r--r--ext/-test-/rational/depend1
-rw-r--r--ext/-test-/string/fstring.c4
-rw-r--r--ext/-test-/string/set_len.c10
-rw-r--r--ext/bigdecimal/bigdecimal.c1366
-rw-r--r--ext/bigdecimal/bigdecimal.gemspec2
-rw-r--r--ext/bigdecimal/bigdecimal.h48
-rw-r--r--ext/bigdecimal/extconf.rb2
-rw-r--r--ext/bigdecimal/lib/bigdecimal/util.rb6
-rw-r--r--ext/bigdecimal/missing.h43
-rw-r--r--ext/coverage/coverage.c54
-rw-r--r--ext/coverage/depend5
-rw-r--r--ext/date/date.gemspec25
-rw-r--r--ext/date/date_core.c201
-rw-r--r--ext/date/date_parse.c72
-rw-r--r--ext/date/date_strptime.c105
-rw-r--r--ext/date/lib/date.rb2
-rw-r--r--ext/date/zonetab.h2
-rw-r--r--ext/date/zonetab.list2
-rw-r--r--ext/digest/digest_conf.rb2
-rw-r--r--ext/digest/lib/digest/version.rb2
-rw-r--r--ext/digest/md5/depend1
-rw-r--r--ext/digest/sha1/depend1
-rw-r--r--ext/digest/sha2/depend1
-rw-r--r--ext/erb/escape/escape.c95
-rw-r--r--ext/erb/escape/extconf.rb7
-rw-r--r--ext/etc/etc.c10
-rwxr-xr-xext/extmk.rb52
-rw-r--r--ext/fcntl/fcntl.gemspec2
-rw-r--r--ext/fiddle/closure.c137
-rw-r--r--ext/fiddle/conversions.c20
-rw-r--r--ext/fiddle/extconf.rb30
-rw-r--r--ext/fiddle/extlibs13
-rw-r--r--ext/fiddle/fiddle.c214
-rw-r--r--ext/fiddle/fiddle.gemspec3
-rw-r--r--ext/fiddle/fiddle.h21
-rw-r--r--ext/fiddle/handle.c59
-rw-r--r--ext/fiddle/lib/fiddle.rb35
-rw-r--r--ext/fiddle/lib/fiddle/closure.rb25
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb18
-rw-r--r--ext/fiddle/lib/fiddle/pack.rb35
-rw-r--r--ext/fiddle/lib/fiddle/version.rb2
-rw-r--r--ext/io/console/console.c40
-rw-r--r--ext/io/console/depend2
-rw-r--r--ext/io/console/io-console.gemspec2
-rw-r--r--ext/io/console/win32_vk.inc327
-rw-r--r--ext/io/console/win32_vk.list2
-rw-r--r--ext/io/nonblock/io-nonblock.gemspec2
-rw-r--r--ext/io/wait/extconf.rb2
-rw-r--r--ext/io/wait/io-wait.gemspec2
-rw-r--r--ext/json/VERSION2
-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/extconf.rb4
-rw-r--r--ext/nkf/nkf.gemspec2
-rw-r--r--ext/objspace/depend23
-rw-r--r--ext/objspace/lib/objspace.rb97
-rw-r--r--ext/objspace/object_tracing.c7
-rw-r--r--ext/objspace/objspace.c1
-rw-r--r--ext/objspace/objspace_dump.c140
-rw-r--r--ext/openssl/History.md105
-rw-r--r--ext/openssl/extconf.rb108
-rw-r--r--ext/openssl/lib/openssl/pkey.rb20
-rw-r--r--ext/openssl/lib/openssl/ssl.rb5
-rw-r--r--ext/openssl/lib/openssl/version.rb2
-rw-r--r--ext/openssl/openssl.gemspec2
-rw-r--r--ext/openssl/ossl.h6
-rw-r--r--ext/openssl/ossl_asn1.c19
-rw-r--r--ext/openssl/ossl_bn.c36
-rw-r--r--ext/openssl/ossl_cipher.c3
-rw-r--r--ext/openssl/ossl_hmac.c8
-rw-r--r--ext/openssl/ossl_kdf.c6
-rw-r--r--ext/openssl/ossl_pkey.c50
-rw-r--r--ext/openssl/ossl_pkey.h2
-rw-r--r--ext/openssl/ossl_pkey_dh.c12
-rw-r--r--ext/openssl/ossl_pkey_dsa.c14
-rw-r--r--ext/openssl/ossl_pkey_ec.c70
-rw-r--r--ext/openssl/ossl_pkey_rsa.c12
-rw-r--r--ext/openssl/ossl_ssl.c183
-rw-r--r--ext/openssl/ossl_ssl_session.c4
-rw-r--r--ext/openssl/ossl_x509cert.c6
-rw-r--r--ext/openssl/ossl_x509crl.c6
-rw-r--r--ext/openssl/ossl_x509req.c6
-rw-r--r--ext/openssl/ossl_x509revoked.c6
-rw-r--r--ext/pathname/lib/pathname.rb4
-rw-r--r--ext/pathname/pathname.gemspec2
-rw-r--r--ext/psych/extconf.rb39
-rw-r--r--ext/psych/extlibs11
-rw-r--r--ext/psych/lib/psych/parser.rb13
-rw-r--r--ext/psych/lib/psych/scalar_scanner.rb2
-rw-r--r--ext/psych/lib/psych/versions.rb4
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb4
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb16
-rw-r--r--ext/psych/psych_parser.c22
-rw-r--r--ext/pty/depend1
-rw-r--r--ext/pty/lib/expect.rb16
-rw-r--r--ext/pty/pty.c2
-rw-r--r--ext/racc/cparse/cparse.c4
-rw-r--r--ext/readline/readline-ext.gemspec2
-rw-r--r--ext/ripper/depend9
-rw-r--r--ext/ripper/eventids2.c19
-rw-r--r--ext/ripper/lib/ripper/lexer.rb9
-rw-r--r--ext/ripper/tools/preproc.rb2
-rw-r--r--ext/socket/ancdata.c4
-rw-r--r--ext/socket/basicsocket.c2
-rw-r--r--ext/socket/depend15
-rw-r--r--ext/socket/extconf.rb23
-rw-r--r--ext/socket/init.c6
-rw-r--r--ext/socket/lib/socket.rb9
-rw-r--r--ext/socket/mkconstants.rb7
-rw-r--r--ext/socket/option.c10
-rw-r--r--ext/socket/raddrinfo.c32
-rw-r--r--ext/socket/rubysocket.h12
-rw-r--r--ext/socket/socket.c10
-rw-r--r--ext/socket/udpsocket.c2
-rw-r--r--ext/socket/unixserver.c4
-rw-r--r--ext/socket/unixsocket.c8
-rw-r--r--ext/stringio/stringio.c318
-rw-r--r--ext/strscan/extconf.rb11
-rw-r--r--ext/strscan/strscan.c178
-rw-r--r--ext/strscan/strscan.gemspec23
-rw-r--r--ext/syslog/syslog.gemspec2
-rw-r--r--ext/win32/lib/win32/registry.rb13
-rw-r--r--ext/win32ole/win32ole.gemspec2
-rw-r--r--ext/zlib/zlib.c2
-rw-r--r--file.c803
-rw-r--r--gc.c837
-rw-r--r--gc.h10
-rw-r--r--gc.rb60
-rw-r--r--gems/bundled_gems22
-rw-r--r--gems/lib/core_assertions.rb1
-rw-r--r--gems/lib/envutil.rb1
-rw-r--r--gems/lib/rake/extensiontask.rb (renamed from tool/dummy-rake-compiler/rake/extensiontask.rb)5
-rw-r--r--hash.c286
-rw-r--r--hrtime.h1
-rw-r--r--id_table.h5
-rw-r--r--include/ruby/assert.h2
-rw-r--r--include/ruby/debug.h35
-rw-r--r--include/ruby/fiber/scheduler.h78
-rw-r--r--include/ruby/internal/abi.h9
-rw-r--r--include/ruby/internal/anyargs.h37
-rw-r--r--include/ruby/internal/arithmetic.h3
-rw-r--r--include/ruby/internal/attr/nodiscard.h2
-rw-r--r--include/ruby/internal/attr/nonstring.h32
-rw-r--r--include/ruby/internal/config.h2
-rw-r--r--include/ruby/internal/core/robject.h56
-rw-r--r--include/ruby/internal/eval.h33
-rw-r--r--include/ruby/internal/fl_type.h19
-rw-r--r--include/ruby/internal/gc.h13
-rw-r--r--include/ruby/internal/intern/class.h12
-rw-r--r--include/ruby/internal/intern/cont.h22
-rw-r--r--include/ruby/internal/intern/file.h2
-rw-r--r--include/ruby/internal/intern/gc.h4
-rw-r--r--include/ruby/internal/intern/object.h4
-rw-r--r--include/ruby/internal/intern/select/posix.h2
-rw-r--r--include/ruby/internal/memory.h6
-rw-r--r--include/ruby/internal/scan_args.h2
-rw-r--r--include/ruby/internal/special_consts.h87
-rw-r--r--include/ruby/io.h55
-rw-r--r--include/ruby/io/buffer.h29
-rw-r--r--include/ruby/memory_view.h2
-rw-r--r--include/ruby/onigmo.h8
-rw-r--r--include/ruby/random.h63
-rw-r--r--include/ruby/st.h2
-rw-r--r--include/ruby/util.h2
-rw-r--r--include/ruby/win32.h25
-rw-r--r--inits.c4
-rw-r--r--insns.def65
-rw-r--r--internal.h6
-rw-r--r--internal/array.h1
-rw-r--r--internal/basic_operators.h64
-rw-r--r--internal/class.h59
-rw-r--r--internal/cmdlineopt.h3
-rw-r--r--internal/compar.h32
-rw-r--r--internal/cont.h8
-rw-r--r--internal/encoding.h4
-rw-r--r--internal/eval.h1
-rw-r--r--internal/gc.h6
-rw-r--r--internal/hash.h1
-rw-r--r--internal/numeric.h8
-rw-r--r--internal/object.h22
-rw-r--r--internal/parse.h2
-rw-r--r--internal/string.h4
-rw-r--r--internal/thread.h2
-rw-r--r--internal/time.h5
-rw-r--r--internal/variable.h13
-rw-r--r--internal/vm.h2
-rw-r--r--io.c1488
-rw-r--r--io_buffer.c2114
-rw-r--r--iseq.c338
-rw-r--r--iseq.h7
-rw-r--r--lex.c.blt2
-rw-r--r--lib/English.gemspec2
-rw-r--r--lib/abbrev.gemspec2
-rw-r--r--lib/benchmark/version.rb2
-rw-r--r--lib/bundler.rb103
-rw-r--r--lib/bundler/bundler.gemspec18
-rw-r--r--lib/bundler/cli.rb52
-rw-r--r--lib/bundler/cli/add.rb2
-rw-r--r--lib/bundler/cli/binstubs.rb8
-rw-r--r--lib/bundler/cli/check.rb2
-rw-r--r--lib/bundler/cli/common.rb1
-rw-r--r--lib/bundler/cli/console.rb4
-rw-r--r--lib/bundler/cli/doctor.rb10
-rw-r--r--lib/bundler/cli/gem.rb102
-rw-r--r--lib/bundler/cli/info.rb2
-rw-r--r--lib/bundler/cli/init.rb8
-rw-r--r--lib/bundler/cli/install.rb7
-rw-r--r--lib/bundler/cli/lock.rb13
-rw-r--r--lib/bundler/cli/open.rb10
-rw-r--r--lib/bundler/cli/outdated.rb6
-rw-r--r--lib/bundler/cli/platform.rb12
-rw-r--r--lib/bundler/compact_index_client/cache.rb2
-rw-r--r--lib/bundler/compact_index_client/updater.rb79
-rw-r--r--lib/bundler/current_ruby.rb21
-rw-r--r--lib/bundler/definition.rb354
-rw-r--r--lib/bundler/dep_proxy.rb55
-rw-r--r--lib/bundler/dependency.rb106
-rw-r--r--lib/bundler/dsl.rb9
-rw-r--r--lib/bundler/endpoint_specification.rb15
-rw-r--r--lib/bundler/env.rb4
-rw-r--r--lib/bundler/environment_preserver.rb5
-rw-r--r--lib/bundler/errors.rb12
-rw-r--r--lib/bundler/feature_flag.rb2
-rw-r--r--lib/bundler/fetcher.rb24
-rw-r--r--lib/bundler/fetcher/compact_index.rb24
-rw-r--r--lib/bundler/fetcher/dependency.rb8
-rw-r--r--lib/bundler/fetcher/downloader.rb7
-rw-r--r--lib/bundler/fetcher/index.rb3
-rw-r--r--lib/bundler/force_platform.rb18
-rw-r--r--lib/bundler/friendly_errors.rb5
-rw-r--r--lib/bundler/gem_helper.rb7
-rw-r--r--lib/bundler/gem_helpers.rb1
-rw-r--r--lib/bundler/gem_version_promoter.rb151
-rw-r--r--lib/bundler/graph.rb6
-rw-r--r--lib/bundler/incomplete_specification.rb12
-rw-r--r--lib/bundler/index.rb52
-rw-r--r--lib/bundler/injector.rb9
-rw-r--r--lib/bundler/inline.rb18
-rw-r--r--lib/bundler/installer.rb51
-rw-r--r--lib/bundler/installer/parallel_installer.rb38
-rw-r--r--lib/bundler/installer/standalone.rb20
-rw-r--r--lib/bundler/lazy_specification.rb100
-rw-r--r--lib/bundler/lockfile_generator.rb6
-rw-r--r--lib/bundler/lockfile_parser.rb33
-rw-r--r--lib/bundler/man/bundle-add.18
-rw-r--r--lib/bundler/man/bundle-add.1.ronn5
-rw-r--r--lib/bundler/man/bundle-binstubs.12
-rw-r--r--lib/bundler/man/bundle-cache.16
-rw-r--r--lib/bundler/man/bundle-cache.1.ronn4
-rw-r--r--lib/bundler/man/bundle-check.12
-rw-r--r--lib/bundler/man/bundle-clean.14
-rw-r--r--lib/bundler/man/bundle-clean.1.ronn2
-rw-r--r--lib/bundler/man/bundle-config.118
-rw-r--r--lib/bundler/man/bundle-config.1.ronn10
-rw-r--r--lib/bundler/man/bundle-console.153
-rw-r--r--lib/bundler/man/bundle-console.1.ronn44
-rw-r--r--lib/bundler/man/bundle-doctor.12
-rw-r--r--lib/bundler/man/bundle-exec.110
-rw-r--r--lib/bundler/man/bundle-exec.1.ronn10
-rw-r--r--lib/bundler/man/bundle-gem.164
-rw-r--r--lib/bundler/man/bundle-gem.1.ronn10
-rw-r--r--lib/bundler/man/bundle-help.113
-rw-r--r--lib/bundler/man/bundle-help.1.ronn12
-rw-r--r--lib/bundler/man/bundle-info.16
-rw-r--r--lib/bundler/man/bundle-info.1.ronn6
-rw-r--r--lib/bundler/man/bundle-init.16
-rw-r--r--lib/bundler/man/bundle-init.1.ronn2
-rw-r--r--lib/bundler/man/bundle-inject.12
-rw-r--r--lib/bundler/man/bundle-install.131
-rw-r--r--lib/bundler/man/bundle-install.1.ronn29
-rw-r--r--lib/bundler/man/bundle-list.12
-rw-r--r--lib/bundler/man/bundle-lock.12
-rw-r--r--lib/bundler/man/bundle-open.124
-rw-r--r--lib/bundler/man/bundle-open.1.ronn10
-rw-r--r--lib/bundler/man/bundle-outdated.122
-rw-r--r--lib/bundler/man/bundle-outdated.1.ronn21
-rw-r--r--lib/bundler/man/bundle-platform.14
-rw-r--r--lib/bundler/man/bundle-platform.1.ronn2
-rw-r--r--lib/bundler/man/bundle-plugin.181
-rw-r--r--lib/bundler/man/bundle-plugin.1.ronn59
-rw-r--r--lib/bundler/man/bundle-pristine.12
-rw-r--r--lib/bundler/man/bundle-remove.12
-rw-r--r--lib/bundler/man/bundle-show.12
-rw-r--r--lib/bundler/man/bundle-update.12
-rw-r--r--lib/bundler/man/bundle-version.135
-rw-r--r--lib/bundler/man/bundle-version.1.ronn24
-rw-r--r--lib/bundler/man/bundle-viz.12
-rw-r--r--lib/bundler/man/bundle.112
-rw-r--r--lib/bundler/man/bundle.1.ronn8
-rw-r--r--lib/bundler/man/gemfile.586
-rw-r--r--lib/bundler/man/gemfile.5.ronn71
-rw-r--r--lib/bundler/man/index.txt4
-rw-r--r--lib/bundler/match_metadata.rb13
-rw-r--r--lib/bundler/match_remote_metadata.rb29
-rw-r--r--lib/bundler/mirror.rb12
-rw-r--r--lib/bundler/plugin.rb4
-rw-r--r--lib/bundler/plugin/index.rb10
-rw-r--r--lib/bundler/plugin/installer.rb7
-rw-r--r--lib/bundler/plugin/installer/rubygems.rb4
-rw-r--r--lib/bundler/remote_specification.rb14
-rw-r--r--lib/bundler/resolver.rb663
-rw-r--r--lib/bundler/resolver/base.rb107
-rw-r--r--lib/bundler/resolver/candidate.rb94
-rw-r--r--lib/bundler/resolver/incompatibility.rb15
-rw-r--r--lib/bundler/resolver/package.rb72
-rw-r--r--lib/bundler/resolver/root.rb25
-rw-r--r--lib/bundler/resolver/spec_group.rb113
-rw-r--r--lib/bundler/ruby_dsl.rb6
-rw-r--r--lib/bundler/ruby_version.rb6
-rw-r--r--lib/bundler/rubygems_ext.rb98
-rw-r--r--lib/bundler/rubygems_gem_installer.rb6
-rw-r--r--lib/bundler/rubygems_integration.rb20
-rw-r--r--lib/bundler/runtime.rb2
-rw-r--r--lib/bundler/safe_marshal.rb31
-rw-r--r--lib/bundler/settings.rb15
-rw-r--r--lib/bundler/setup.rb5
-rw-r--r--lib/bundler/shared_helpers.rb6
-rw-r--r--lib/bundler/source.rb2
-rw-r--r--lib/bundler/source/git.rb85
-rw-r--r--lib/bundler/source/git/git_proxy.rb316
-rw-r--r--lib/bundler/source/metadata.rb1
-rw-r--r--lib/bundler/source/path.rb12
-rw-r--r--lib/bundler/source/path/installer.rb23
-rw-r--r--lib/bundler/source/rubygems.rb107
-rw-r--r--lib/bundler/source_list.rb14
-rw-r--r--lib/bundler/spec_set.rb66
-rw-r--r--lib/bundler/templates/Executable.bundler10
-rw-r--r--lib/bundler/templates/gems.rb5
-rw-r--r--lib/bundler/templates/newgem/Cargo.toml.tt7
-rw-r--r--lib/bundler/templates/newgem/Gemfile.tt3
-rw-r--r--lib/bundler/templates/newgem/README.md.tt10
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt13
-rw-r--r--lib/bundler/templates/newgem/bin/console.tt4
-rw-r--r--lib/bundler/templates/newgem/circleci/config.yml.tt12
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt15
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt10
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt6
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt5
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.c.tt2
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt12
-rw-r--r--lib/bundler/templates/newgem/github/workflows/main.yml.tt10
-rw-r--r--lib/bundler/templates/newgem/gitignore.tt3
-rw-r--r--lib/bundler/templates/newgem/gitlab-ci.yml.tt17
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt11
-rw-r--r--lib/bundler/templates/newgem/travis.yml.tt6
-rw-r--r--lib/bundler/ui/rg_proxy.rb2
-rw-r--r--lib/bundler/ui/shell.rb47
-rw-r--r--lib/bundler/ui/silent.rb26
-rw-r--r--lib/bundler/uri_normalizer.rb23
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool.rb4
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb6
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb1
-rw-r--r--lib/bundler/vendor/fileutils/lib/fileutils.rb1760
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo.rb11
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb88
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb255
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb36
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb66
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb62
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb63
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb61
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb126
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb46
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb36
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb164
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/errors.rb149
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb112
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb67
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb839
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb46
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/state.rb58
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub.rb31
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb20
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb189
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb182
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb150
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb43
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb121
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb45
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb19
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb60
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb105
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb3
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb129
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb411
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb248
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb178
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/basic.rb2
-rw-r--r--lib/bundler/vendor/tmpdir/lib/tmpdir.rb154
-rw-r--r--lib/bundler/vendor/uri/lib/uri.rb5
-rw-r--r--lib/bundler/vendor/uri/lib/uri/common.rb80
-rw-r--r--lib/bundler/vendor/uri/lib/uri/file.rb8
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ftp.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/generic.rb34
-rw-r--r--lib/bundler/vendor/uri/lib/uri/http.rb42
-rw-r--r--lib/bundler/vendor/uri/lib/uri/https.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ldap.rb2
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ldaps.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/mailto.rb4
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb24
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb17
-rw-r--r--lib/bundler/vendor/uri/lib/uri/version.rb2
-rw-r--r--lib/bundler/vendor/uri/lib/uri/ws.rb3
-rw-r--r--lib/bundler/vendor/uri/lib/uri/wss.rb3
-rw-r--r--lib/bundler/vendored_molinillo.rb4
-rw-r--r--lib/bundler/vendored_persistent.rb34
-rw-r--r--lib/bundler/vendored_pub_grub.rb (renamed from lib/bundler/vendored_tmpdir.rb)2
-rw-r--r--lib/bundler/version.rb6
-rw-r--r--lib/bundler/version_ranges.rb122
-rw-r--r--lib/bundler/worker.rb12
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/cookie.rb49
-rw-r--r--lib/cgi/core.rb45
-rw-r--r--lib/cgi/util.rb4
-rw-r--r--lib/csv.rb489
-rw-r--r--lib/csv/fields_converter.rb5
-rw-r--r--lib/csv/input_record_separator.rb15
-rw-r--r--lib/csv/parser.rb293
-rw-r--r--lib/csv/row.rb231
-rw-r--r--lib/csv/table.rb626
-rw-r--r--lib/csv/version.rb2
-rw-r--r--lib/csv/writer.rb10
-rw-r--r--lib/delegate.rb2
-rw-r--r--lib/did_you_mean/spell_checkers/method_name_checker.rb7
-rw-r--r--lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb2
-rw-r--r--lib/did_you_mean/version.rb2
-rw-r--r--lib/drb/version.rb2
-rw-r--r--lib/erb.gemspec13
-rw-r--r--lib/erb.rb579
-rw-r--r--lib/erb/compiler.rb471
-rw-r--r--lib/erb/def_method.rb46
-rw-r--r--lib/erb/util.rb62
-rw-r--r--lib/erb/version.rb2
-rw-r--r--lib/error_highlight/base.rb3
-rw-r--r--lib/error_highlight/core_ext.rb9
-rw-r--r--lib/error_highlight/version.rb2
-rw-r--r--lib/fileutils.rb155
-rw-r--r--lib/forwardable.rb4
-rw-r--r--lib/getoptlong.rb2
-rw-r--r--lib/ipaddr.rb6
-rw-r--r--lib/irb.rb91
-rw-r--r--lib/irb/cmd/backtrace.rb21
-rw-r--r--lib/irb/cmd/break.rb21
-rw-r--r--lib/irb/cmd/catch.rb21
-rw-r--r--lib/irb/cmd/chws.rb6
-rw-r--r--lib/irb/cmd/continue.rb17
-rw-r--r--lib/irb/cmd/debug.rb136
-rw-r--r--lib/irb/cmd/delete.rb17
-rw-r--r--lib/irb/cmd/edit.rb61
-rw-r--r--lib/irb/cmd/finish.rb17
-rw-r--r--lib/irb/cmd/help.rb14
-rw-r--r--lib/irb/cmd/info.rb31
-rw-r--r--lib/irb/cmd/irb_info.rb37
-rw-r--r--lib/irb/cmd/load.rb34
-rw-r--r--lib/irb/cmd/ls.rb13
-rw-r--r--lib/irb/cmd/measure.rb3
-rw-r--r--lib/irb/cmd/next.rb17
-rw-r--r--lib/irb/cmd/nop.rb24
-rw-r--r--lib/irb/cmd/pushws.rb9
-rw-r--r--lib/irb/cmd/show_cmds.rb39
-rw-r--r--lib/irb/cmd/show_source.rb103
-rw-r--r--lib/irb/cmd/step.rb17
-rw-r--r--lib/irb/cmd/subirb.rb38
-rw-r--r--lib/irb/cmd/whereami.rb3
-rw-r--r--lib/irb/color.rb66
-rw-r--r--lib/irb/completion.rb136
-rw-r--r--lib/irb/context.rb69
-rw-r--r--lib/irb/ext/multi-irb.rb1
-rw-r--r--lib/irb/ext/save-history.rb10
-rw-r--r--lib/irb/extend-command.rb123
-rw-r--r--lib/irb/init.rb48
-rw-r--r--lib/irb/input-method.rb26
-rw-r--r--lib/irb/irb.gemspec6
-rw-r--r--lib/irb/lc/error.rb5
-rw-r--r--lib/irb/lc/help-message2
-rw-r--r--lib/irb/lc/ja/error.rb5
-rw-r--r--lib/irb/ruby-lex.rb119
-rw-r--r--lib/irb/version.rb4
-rw-r--r--lib/logger.rb10
-rw-r--r--lib/logger/formatter.rb5
-rw-r--r--lib/logger/log_device.rb6
-rw-r--r--lib/logger/logger.gemspec1
-rw-r--r--lib/logger/version.rb2
-rw-r--r--lib/mkmf.rb23
-rw-r--r--lib/mutex_m.rb2
-rw-r--r--lib/net/http.rb2053
-rw-r--r--lib/net/http/backward.rb2
-rw-r--r--lib/net/http/exceptions.rb2
-rw-r--r--lib/net/http/generic_request.rb109
-rw-r--r--lib/net/http/header.rb786
-rw-r--r--lib/net/http/net-http.gemspec13
-rw-r--r--lib/net/http/proxy_delta.rb2
-rw-r--r--lib/net/http/request.rb79
-rw-r--r--lib/net/http/requests.rb352
-rw-r--r--lib/net/http/response.rb200
-rw-r--r--lib/net/http/responses.rb1000
-rw-r--r--lib/net/http/status.rb13
-rw-r--r--lib/net/https.rb2
-rw-r--r--lib/net/protocol.rb75
-rw-r--r--lib/open-uri.gemspec4
-rw-r--r--lib/open-uri.rb35
-rw-r--r--lib/open3/version.rb2
-rw-r--r--lib/optparse.rb41
-rw-r--r--lib/pp.gemspec2
-rw-r--r--lib/pp.rb20
-rw-r--r--lib/pstore.rb10
-rw-r--r--lib/racc/info.rb2
-rw-r--r--lib/racc/racc.gemspec2
-rw-r--r--lib/racc/statetransitiontable.rb2
-rw-r--r--lib/random/formatter.rb62
-rw-r--r--lib/rdoc/generator/markup.rb2
-rw-r--r--lib/rdoc/generator/template/darkfish/_head.rhtml20
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml6
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml29
-rw-r--r--lib/rdoc/generator/template/darkfish/class.rhtml42
-rw-r--r--lib/rdoc/generator/template/darkfish/index.rhtml2
-rw-r--r--lib/rdoc/generator/template/darkfish/js/darkfish.js2
-rw-r--r--lib/rdoc/generator/template/darkfish/js/search.js2
-rw-r--r--lib/rdoc/generator/template/darkfish/table_of_contents.rhtml4
-rw-r--r--lib/rdoc/markdown.rb466
-rw-r--r--lib/rdoc/markdown/literals.rb109
-rw-r--r--lib/rdoc/markup/attribute_manager.rb33
-rw-r--r--lib/rdoc/markup/to_html.rb26
-rw-r--r--lib/rdoc/markup/to_rdoc.rb23
-rw-r--r--lib/rdoc/options.rb12
-rw-r--r--lib/rdoc/parser.rb17
-rw-r--r--lib/rdoc/parser/c.rb17
-rw-r--r--lib/rdoc/parser/ruby.rb10
-rw-r--r--lib/rdoc/rd/block_parser.rb20
-rw-r--r--lib/rdoc/rdoc.rb16
-rw-r--r--lib/rdoc/ri/driver.rb10
-rw-r--r--lib/rdoc/store.rb45
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/reline.rb60
-rw-r--r--lib/reline/ansi.rb2
-rw-r--r--lib/reline/config.rb75
-rw-r--r--lib/reline/general_io.rb6
-rw-r--r--lib/reline/line_editor.rb8
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/resolv-replace.gemspec2
-rw-r--r--lib/resolv.gemspec2
-rw-r--r--lib/resolv.rb6
-rw-r--r--lib/ruby_vm/mjit/c_pointer.rb329
-rw-r--r--lib/ruby_vm/mjit/c_type.rb91
-rw-r--r--lib/ruby_vm/mjit/compiler.rb952
-rw-r--r--lib/ruby_vm/mjit/hooks.rb32
-rw-r--r--lib/rubygems.rb44
-rw-r--r--lib/rubygems/available_set.rb1
-rw-r--r--lib/rubygems/basic_specification.rb1
-rw-r--r--lib/rubygems/bundler_version_finder.rb4
-rw-r--r--lib/rubygems/command.rb23
-rw-r--r--lib/rubygems/command_manager.rb31
-rw-r--r--lib/rubygems/commands/build_command.rb4
-rw-r--r--lib/rubygems/commands/cert_command.rb1
-rw-r--r--lib/rubygems/commands/check_command.rb1
-rw-r--r--lib/rubygems/commands/cleanup_command.rb1
-rw-r--r--lib/rubygems/commands/contents_command.rb1
-rw-r--r--lib/rubygems/commands/dependency_command.rb1
-rw-r--r--lib/rubygems/commands/environment_command.rb1
-rw-r--r--lib/rubygems/commands/exec_command.rb249
-rw-r--r--lib/rubygems/commands/fetch_command.rb3
-rw-r--r--lib/rubygems/commands/generate_index_command.rb1
-rw-r--r--lib/rubygems/commands/help_command.rb7
-rw-r--r--lib/rubygems/commands/install_command.rb11
-rw-r--r--lib/rubygems/commands/list_command.rb1
-rw-r--r--lib/rubygems/commands/lock_command.rb1
-rw-r--r--lib/rubygems/commands/mirror_command.rb1
-rw-r--r--lib/rubygems/commands/open_command.rb1
-rw-r--r--lib/rubygems/commands/outdated_command.rb1
-rw-r--r--lib/rubygems/commands/owner_command.rb9
-rw-r--r--lib/rubygems/commands/pristine_command.rb10
-rw-r--r--lib/rubygems/commands/push_command.rb1
-rw-r--r--lib/rubygems/commands/query_command.rb1
-rw-r--r--lib/rubygems/commands/rdoc_command.rb1
-rw-r--r--lib/rubygems/commands/search_command.rb1
-rw-r--r--lib/rubygems/commands/server_command.rb1
-rw-r--r--lib/rubygems/commands/setup_command.rb7
-rw-r--r--lib/rubygems/commands/signin_command.rb1
-rw-r--r--lib/rubygems/commands/signout_command.rb1
-rw-r--r--lib/rubygems/commands/sources_command.rb1
-rw-r--r--lib/rubygems/commands/specification_command.rb1
-rw-r--r--lib/rubygems/commands/stale_command.rb1
-rw-r--r--lib/rubygems/commands/uninstall_command.rb4
-rw-r--r--lib/rubygems/commands/unpack_command.rb3
-rw-r--r--lib/rubygems/commands/update_command.rb9
-rw-r--r--lib/rubygems/commands/which_command.rb1
-rw-r--r--lib/rubygems/commands/yank_command.rb1
-rw-r--r--lib/rubygems/config_file.rb34
-rw-r--r--lib/rubygems/core_ext/kernel_gem.rb5
-rw-r--r--lib/rubygems/core_ext/kernel_require.rb222
-rw-r--r--lib/rubygems/core_ext/kernel_warn.rb70
-rw-r--r--lib/rubygems/core_ext/tcpsocket_init.rb2
-rw-r--r--lib/rubygems/defaults.rb17
-rw-r--r--lib/rubygems/dependency.rb8
-rw-r--r--lib/rubygems/dependency_installer.rb49
-rw-r--r--lib/rubygems/dependency_list.rb1
-rw-r--r--lib/rubygems/deprecate.rb5
-rw-r--r--lib/rubygems/doctor.rb1
-rw-r--r--lib/rubygems/errors.rb1
-rw-r--r--lib/rubygems/exceptions.rb10
-rw-r--r--lib/rubygems/ext.rb1
-rw-r--r--lib/rubygems/ext/build_error.rb1
-rw-r--r--lib/rubygems/ext/builder.rb28
-rw-r--r--lib/rubygems/ext/cargo_builder.rb257
-rw-r--r--lib/rubygems/ext/cargo_builder/link_flag_converter.rb14
-rw-r--r--lib/rubygems/ext/configure_builder.rb1
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb6
-rw-r--r--lib/rubygems/ext/rake_builder.rb8
-rw-r--r--lib/rubygems/gem_runner.rb1
-rw-r--r--lib/rubygems/gemcutter_utilities.rb71
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener.rb105
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb163
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_poller.rb78
-rw-r--r--lib/rubygems/indexer.rb3
-rw-r--r--lib/rubygems/install_default_message.rb1
-rw-r--r--lib/rubygems/install_message.rb1
-rw-r--r--lib/rubygems/install_update_options.rb1
-rw-r--r--lib/rubygems/installer.rb24
-rw-r--r--lib/rubygems/local_remote_options.rb1
-rw-r--r--lib/rubygems/mock_gem_ui.rb1
-rw-r--r--lib/rubygems/name_tuple.rb1
-rw-r--r--lib/rubygems/optparse/lib/optparse.rb35
-rw-r--r--lib/rubygems/package.rb17
-rw-r--r--lib/rubygems/package/digest_io.rb1
-rw-r--r--lib/rubygems/package/file_source.rb1
-rw-r--r--lib/rubygems/package/io_source.rb1
-rw-r--r--lib/rubygems/package/old.rb1
-rw-r--r--lib/rubygems/package/source.rb1
-rw-r--r--lib/rubygems/package/tar_header.rb25
-rw-r--r--lib/rubygems/package/tar_reader.rb29
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb96
-rw-r--r--lib/rubygems/package/tar_writer.rb1
-rw-r--r--lib/rubygems/package_task.rb1
-rw-r--r--lib/rubygems/path_support.rb1
-rw-r--r--lib/rubygems/platform.rb44
-rw-r--r--lib/rubygems/psych_tree.rb1
-rw-r--r--lib/rubygems/query_utils.rb4
-rw-r--r--lib/rubygems/rdoc.rb1
-rw-r--r--lib/rubygems/remote_fetcher.rb1
-rw-r--r--lib/rubygems/request.rb1
-rw-r--r--lib/rubygems/request/http_pool.rb1
-rw-r--r--lib/rubygems/request/https_pool.rb1
-rw-r--r--lib/rubygems/request_set.rb5
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb212
-rw-r--r--lib/rubygems/request_set/lockfile.rb1
-rw-r--r--lib/rubygems/request_set/lockfile/parser.rb3
-rw-r--r--lib/rubygems/request_set/lockfile/tokenizer.rb2
-rw-r--r--lib/rubygems/requirement.rb17
-rw-r--r--lib/rubygems/resolver.rb3
-rw-r--r--lib/rubygems/resolver/activation_request.rb1
-rw-r--r--lib/rubygems/resolver/api_set.rb1
-rw-r--r--lib/rubygems/resolver/api_specification.rb1
-rw-r--r--lib/rubygems/resolver/best_set.rb1
-rw-r--r--lib/rubygems/resolver/composed_set.rb1
-rw-r--r--lib/rubygems/resolver/conflict.rb1
-rw-r--r--lib/rubygems/resolver/current_set.rb1
-rw-r--r--lib/rubygems/resolver/dependency_request.rb1
-rw-r--r--lib/rubygems/resolver/git_set.rb1
-rw-r--r--lib/rubygems/resolver/git_specification.rb1
-rw-r--r--lib/rubygems/resolver/index_set.rb1
-rw-r--r--lib/rubygems/resolver/index_specification.rb1
-rw-r--r--lib/rubygems/resolver/installed_specification.rb1
-rw-r--r--lib/rubygems/resolver/installer_set.rb7
-rw-r--r--lib/rubygems/resolver/local_specification.rb1
-rw-r--r--lib/rubygems/resolver/lock_set.rb1
-rw-r--r--lib/rubygems/resolver/lock_specification.rb1
-rw-r--r--lib/rubygems/resolver/molinillo.rb1
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb58
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb2
-rw-r--r--lib/rubygems/resolver/requirement_list.rb1
-rw-r--r--lib/rubygems/resolver/set.rb1
-rw-r--r--lib/rubygems/resolver/source_set.rb2
-rw-r--r--lib/rubygems/resolver/spec_specification.rb1
-rw-r--r--lib/rubygems/resolver/specification.rb1
-rw-r--r--lib/rubygems/resolver/stats.rb3
-rw-r--r--lib/rubygems/resolver/vendor_set.rb1
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb1
-rw-r--r--lib/rubygems/s3_uri_signer.rb6
-rw-r--r--lib/rubygems/safe_yaml.rb2
-rw-r--r--lib/rubygems/security.rb20
-rw-r--r--lib/rubygems/security/policies.rb81
-rw-r--r--lib/rubygems/security/policy.rb1
-rw-r--r--lib/rubygems/security/signer.rb1
-rw-r--r--lib/rubygems/security/trust_dir.rb3
-rw-r--r--lib/rubygems/security_option.rb1
-rw-r--r--lib/rubygems/shellwords.rb3
-rw-r--r--lib/rubygems/source.rb4
-rw-r--r--lib/rubygems/source/git.rb2
-rw-r--r--lib/rubygems/source/installed.rb1
-rw-r--r--lib/rubygems/source/local.rb1
-rw-r--r--lib/rubygems/source/lock.rb1
-rw-r--r--lib/rubygems/source/specific_file.rb1
-rw-r--r--lib/rubygems/source/vendor.rb1
-rw-r--r--lib/rubygems/spec_fetcher.rb1
-rw-r--r--lib/rubygems/specification.rb94
-rw-r--r--lib/rubygems/specification_policy.rb21
-rw-r--r--lib/rubygems/stub_specification.rb18
-rw-r--r--lib/rubygems/text.rb2
-rw-r--r--lib/rubygems/tsort/lib/tsort.rb618
-rw-r--r--lib/rubygems/uninstaller.rb1
-rw-r--r--lib/rubygems/update_suggestion.rb69
-rw-r--r--lib/rubygems/user_interaction.rb6
-rw-r--r--lib/rubygems/util.rb7
-rw-r--r--lib/rubygems/util/licenses.rb5
-rw-r--r--lib/rubygems/util/list.rb1
-rw-r--r--lib/rubygems/validator.rb1
-rw-r--r--lib/rubygems/version.rb4
-rw-r--r--lib/rubygems/version_option.rb1
-rw-r--r--lib/securerandom.gemspec2
-rw-r--r--lib/set.rb4
-rw-r--r--lib/set/set.gemspec2
-rw-r--r--lib/syntax_suggest/api.rb6
-rw-r--r--lib/syntax_suggest/around_block_scan.rb290
-rw-r--r--lib/syntax_suggest/block_expand.rb111
-rw-r--r--lib/syntax_suggest/capture/before_after_keyword_ends.rb85
-rw-r--r--lib/syntax_suggest/capture/falling_indent_lines.rb71
-rw-r--r--lib/syntax_suggest/capture_code_context.rb30
-rw-r--r--lib/syntax_suggest/clean_document.rb6
-rw-r--r--lib/syntax_suggest/cli.rb5
-rw-r--r--lib/syntax_suggest/code_line.rb8
-rw-r--r--lib/syntax_suggest/core_ext.rb89
-rw-r--r--lib/syntax_suggest/display_code_with_line_numbers.rb6
-rw-r--r--lib/syntax_suggest/display_invalid_blocks.rb1
-rw-r--r--lib/syntax_suggest/parse_blocks_from_indent_line.rb4
-rw-r--r--lib/syntax_suggest/pathname_from_message.rb2
-rw-r--r--lib/syntax_suggest/scan_history.rb134
-rw-r--r--lib/syntax_suggest/syntax_suggest.gemspec6
-rw-r--r--lib/syntax_suggest/version.rb2
-rw-r--r--lib/tempfile.gemspec2
-rw-r--r--lib/tempfile.rb18
-rw-r--r--lib/time.gemspec2
-rw-r--r--lib/time.rb4
-rw-r--r--lib/timeout.rb3
-rw-r--r--lib/tmpdir.gemspec4
-rw-r--r--lib/tmpdir.rb24
-rw-r--r--lib/tsort.gemspec2
-rw-r--r--lib/un.gemspec2
-rw-r--r--lib/un.rb4
-rw-r--r--lib/unicode_normalize/tables.rb77
-rw-r--r--lib/uri/common.rb2
-rw-r--r--lib/uri/generic.rb15
-rw-r--r--lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/uri/rfc3986_parser.rb7
-rw-r--r--lib/uri/version.rb2
-rw-r--r--lib/weakref.rb2
-rw-r--r--lib/yaml/yaml.gemspec2
-rwxr-xr-xlibexec/bundle21
-rwxr-xr-xlibexec/erb12
-rwxr-xr-xlibexec/syntax_suggest7
-rw-r--r--load.c117
-rw-r--r--main.c6
-rw-r--r--man/irb.125
-rw-r--r--marshal.c64
-rw-r--r--method.h12
-rw-r--r--mini_builtin.c2
-rwxr-xr-xmisc/lldb_cruby.py1
-rw-r--r--misc/lldb_rb/rb_base_command.py4
-rw-r--r--mjit.c1053
-rw-r--r--mjit.h59
-rw-r--r--mjit.rb24
-rw-r--r--mjit_c.c43
-rw-r--r--mjit_c.h97
-rw-r--r--mjit_c.rb807
-rw-r--r--mjit_compiler.c596
-rw-r--r--mjit_unit.h29
-rw-r--r--node.c27
-rw-r--r--node.h6
-rw-r--r--numeric.c55
-rw-r--r--numeric.rb74
-rw-r--r--object.c133
-rw-r--r--pack.c36
-rw-r--r--pack.rb298
-rw-r--r--parse.y1081
-rw-r--r--proc.c176
-rw-r--r--process.c118
-rw-r--r--ractor.c122
-rw-r--r--ractor.rb6
-rw-r--r--ractor_core.h9
-rw-r--r--random.c50
-rw-r--r--range.c213
-rw-r--r--rational.c37
-rw-r--r--re.c415
-rw-r--r--regcomp.c2
-rw-r--r--regenc.c15
-rw-r--r--regenc.h14
-rw-r--r--regexec.c822
-rw-r--r--regint.h38
-rw-r--r--regparse.c3
-rw-r--r--ruby.c146
-rw-r--r--rubystub.c1
-rw-r--r--sample/coverage.rb2
-rw-r--r--sample/from.rb2
-rwxr-xr-xsample/mine.rb8
-rw-r--r--sample/mpart.rb44
-rw-r--r--sample/trick2018/02-mame/entry.rb4
-rw-r--r--sample/trick2022/01-tompng/Gemfile2
-rw-r--r--sample/trick2022/01-tompng/Gemfile.lock13
-rw-r--r--sample/trick2022/01-tompng/authors.markdown3
-rw-r--r--sample/trick2022/01-tompng/entry.rb40
-rw-r--r--sample/trick2022/01-tompng/remarks.markdown51
-rw-r--r--sample/trick2022/02-tompng/authors.markdown3
-rw-r--r--sample/trick2022/02-tompng/entry.rb32
-rw-r--r--sample/trick2022/02-tompng/remarks.markdown32
-rw-r--r--sample/trick2022/03-mame/authors.markdown3
-rw-r--r--sample/trick2022/03-mame/entry.rb27
-rw-r--r--sample/trick2022/03-mame/remarks.markdown96
-rw-r--r--sample/trick2022/03-mame/test.txt13
-rw-r--r--sample/trick2022/README.md14
-rw-r--r--sample/uumerge.rb2
-rw-r--r--scheduler.c413
-rw-r--r--shape.c825
-rw-r--r--shape.h232
-rw-r--r--signal.c5
-rw-r--r--siphash.c6
-rw-r--r--spec/README.md20
-rw-r--r--spec/bundler/bundler/bundler_spec.rb197
-rw-r--r--spec/bundler/bundler/cli_spec.rb46
-rw-r--r--spec/bundler/bundler/definition_spec.rb45
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb32
-rw-r--r--spec/bundler/bundler/dependency_spec.rb157
-rw-r--r--spec/bundler/bundler/dsl_spec.rb11
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb25
-rw-r--r--spec/bundler/bundler/env_spec.rb8
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb18
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb30
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb23
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb30
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb13
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb246
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb15
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb38
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb10
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb2
-rw-r--r--spec/bundler/bundler/resolver/candidate_spec.rb21
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb27
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb10
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb11
-rw-r--r--spec/bundler/bundler/settings_spec.rb2
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb11
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb53
-rw-r--r--spec/bundler/bundler/vendored_persistent_spec.rb77
-rw-r--r--spec/bundler/bundler/version_ranges_spec.rb40
-rw-r--r--spec/bundler/cache/git_spec.rb57
-rw-r--r--spec/bundler/commands/add_spec.rb15
-rw-r--r--spec/bundler/commands/binstubs_spec.rb56
-rw-r--r--spec/bundler/commands/cache_spec.rb2
-rw-r--r--spec/bundler/commands/check_spec.rb6
-rw-r--r--spec/bundler/commands/clean_spec.rb7
-rw-r--r--spec/bundler/commands/config_spec.rb32
-rw-r--r--spec/bundler/commands/doctor_spec.rb2
-rw-r--r--spec/bundler/commands/exec_spec.rb39
-rw-r--r--spec/bundler/commands/fund_spec.rb16
-rw-r--r--spec/bundler/commands/help_spec.rb4
-rw-r--r--spec/bundler/commands/info_spec.rb14
-rw-r--r--spec/bundler/commands/init_spec.rb38
-rw-r--r--spec/bundler/commands/inject_spec.rb2
-rw-r--r--spec/bundler/commands/install_spec.rb31
-rw-r--r--spec/bundler/commands/lock_spec.rb697
-rw-r--r--spec/bundler/commands/newgem_spec.rb201
-rw-r--r--spec/bundler/commands/open_spec.rb58
-rw-r--r--spec/bundler/commands/outdated_spec.rb2
-rw-r--r--spec/bundler/commands/platform_spec.rb8
-rw-r--r--spec/bundler/commands/remove_spec.rb4
-rw-r--r--spec/bundler/commands/show_spec.rb2
-rw-r--r--spec/bundler/commands/update_spec.rb270
-rw-r--r--spec/bundler/commands/viz_spec.rb6
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb5
-rw-r--r--spec/bundler/install/binstubs_spec.rb4
-rw-r--r--spec/bundler/install/bundler_spec.rb83
-rw-r--r--spec/bundler/install/deploy_spec.rb125
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb6
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb78
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb109
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb101
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb107
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb503
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb35
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb42
-rw-r--r--spec/bundler/install/gems/flex_spec.rb77
-rw-r--r--spec/bundler/install/gems/fund_spec.rb16
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb8
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb192
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb38
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb205
-rw-r--r--spec/bundler/install/git_spec.rb72
-rw-r--r--spec/bundler/install/global_cache_spec.rb4
-rw-r--r--spec/bundler/install/yanked_spec.rb72
-rw-r--r--spec/bundler/lock/git_spec.rb127
-rw-r--r--spec/bundler/lock/lockfile_spec.rb261
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb5
-rw-r--r--spec/bundler/plugins/install_spec.rb30
-rw-r--r--spec/bundler/plugins/source/example_spec.rb8
-rw-r--r--spec/bundler/quality_es_spec.rb4
-rw-r--r--spec/bundler/quality_spec.rb24
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb10
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb357
-rw-r--r--spec/bundler/realworld/ffi_spec.rb2
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb10
-rw-r--r--spec/bundler/realworld/git_spec.rb11
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb10
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb19
-rw-r--r--spec/bundler/resolver/basic_spec.rb74
-rw-r--r--spec/bundler/resolver/platform_spec.rb141
-rw-r--r--spec/bundler/runtime/inline_spec.rb168
-rw-r--r--spec/bundler/runtime/platform_spec.rb53
-rw-r--r--spec/bundler/runtime/self_management_spec.rb2
-rw-r--r--spec/bundler/runtime/setup_spec.rb47
-rw-r--r--spec/bundler/spec_helper.rb34
-rw-r--r--spec/bundler/support/api_request_limit_hax.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index.rb118
-rw-r--r--spec/bundler/support/artifice/compact_index_api_missing.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_creds_diff_host.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_extra.rb35
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api.rb50
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_no_gem.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_precompiled_before.rb25
-rw-r--r--spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_rate_limited.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_redirects.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_dependencies.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint.rb113
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb7
-rw-r--r--spec/bundler/support/artifice/endpoint_api_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_creds_diff_host.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_api.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_fallback.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail.rb11
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb4
-rw-r--r--spec/bundler/support/artifice/endpoint_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/endpoint_timeout.rb6
-rw-r--r--spec/bundler/support/artifice/fail.rb11
-rw-r--r--spec/bundler/support/artifice/helpers/artifice.rb30
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb118
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra.rb33
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra_api.rb48
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb112
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_extra.rb29
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_fallback.rb15
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb9
-rw-r--r--spec/bundler/support/artifice/helpers/rack_request.rb100
-rw-r--r--spec/bundler/support/artifice/used_cassettes.txt20908
-rw-r--r--spec/bundler/support/artifice/vcr.rb33
-rw-r--r--spec/bundler/support/artifice/windows.rb7
-rw-r--r--spec/bundler/support/builders.rb34
-rw-r--r--spec/bundler/support/bundle.rb2
-rw-r--r--spec/bundler/support/filters.rb5
-rw-r--r--spec/bundler/support/hax.rb25
-rw-r--r--spec/bundler/support/helpers.rb43
-rw-r--r--spec/bundler/support/indexes.rb38
-rw-r--r--spec/bundler/support/matchers.rb14
-rw-r--r--spec/bundler/support/path.rb36
-rw-r--r--spec/bundler/support/platforms.rb36
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb2
-rw-r--r--spec/bundler/support/sudo.rb22
-rw-r--r--spec/bundler/update/gems/fund_spec.rb16
-rw-r--r--spec/bundler/update/git_spec.rb4
-rwxr-xr-xspec/mspec/lib/mspec/commands/mkspec.rb16
-rw-r--r--spec/mspec/lib/mspec/runner/actions/leakchecker.rb3
-rw-r--r--spec/mspec/lib/mspec/utils/name_map.rb7
-rw-r--r--spec/mspec/lib/mspec/utils/script.rb1
-rw-r--r--spec/mspec/spec/commands/mkspec_spec.rb2
-rw-r--r--spec/mspec/spec/utils/script_spec.rb5
-rwxr-xr-xspec/mspec/tool/tag_from_output.rb26
-rw-r--r--spec/ruby/.rubocop.yml7
-rw-r--r--spec/ruby/CONTRIBUTING.md5
-rw-r--r--spec/ruby/README.md6
-rw-r--r--spec/ruby/core/array/keep_if_spec.rb1
-rw-r--r--spec/ruby/core/array/pack/c_spec.rb14
-rw-r--r--spec/ruby/core/array/pack/m_spec.rb10
-rw-r--r--spec/ruby/core/array/pack/shared/basic.rb40
-rw-r--r--spec/ruby/core/array/pack/shared/float.rb66
-rw-r--r--spec/ruby/core/array/pack/shared/integer.rb96
-rw-r--r--spec/ruby/core/array/pack/shared/numeric_basic.rb10
-rw-r--r--spec/ruby/core/array/pack/shared/unicode.rb14
-rw-r--r--spec/ruby/core/array/pack/w_spec.rb14
-rw-r--r--spec/ruby/core/array/shared/unshift.rb18
-rw-r--r--spec/ruby/core/array/values_at_spec.rb1
-rw-r--r--spec/ruby/core/array/zip_spec.rb6
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb68
-rw-r--r--spec/ruby/core/builtin_constants/builtin_constants_spec.rb6
-rw-r--r--spec/ruby/core/class/attached_object_spec.rb31
-rw-r--r--spec/ruby/core/class/subclasses_spec.rb22
-rw-r--r--spec/ruby/core/complex/polar_spec.rb16
-rw-r--r--spec/ruby/core/data/constants_spec.rb14
-rw-r--r--spec/ruby/core/dir/fixtures/common.rb1
-rw-r--r--spec/ruby/core/dir/glob_spec.rb2
-rw-r--r--spec/ruby/core/dir/home_spec.rb43
-rw-r--r--spec/ruby/core/dir/mkdir_spec.rb18
-rw-r--r--spec/ruby/core/dir/shared/chroot.rb9
-rw-r--r--spec/ruby/core/dir/shared/glob.rb5
-rw-r--r--spec/ruby/core/encoding/replicate_spec.rb108
-rw-r--r--spec/ruby/core/enumerable/each_cons_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/each_slice_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/sum_spec.rb17
-rw-r--r--spec/ruby/core/enumerable/zip_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/compact_spec.rb11
-rw-r--r--spec/ruby/core/enumerator/lazy/lazy_spec.rb10
-rw-r--r--spec/ruby/core/env/shared/update.rb7
-rw-r--r--spec/ruby/core/false/case_compare_spec.rb14
-rw-r--r--spec/ruby/core/fiber/blocking_spec.rb17
-rw-r--r--spec/ruby/core/fiber/storage_spec.rb117
-rw-r--r--spec/ruby/core/file/atime_spec.rb6
-rw-r--r--spec/ruby/core/file/ctime_spec.rb4
-rw-r--r--spec/ruby/core/file/mtime_spec.rb20
-rw-r--r--spec/ruby/core/file/shared/path.rb14
-rw-r--r--spec/ruby/core/file/utime_spec.rb34
-rw-r--r--spec/ruby/core/float/comparison_spec.rb35
-rw-r--r--spec/ruby/core/float/divmod_spec.rb2
-rw-r--r--spec/ruby/core/float/gt_spec.rb21
-rw-r--r--spec/ruby/core/float/gte_spec.rb21
-rw-r--r--spec/ruby/core/float/lt_spec.rb21
-rw-r--r--spec/ruby/core/float/lte_spec.rb21
-rw-r--r--spec/ruby/core/float/shared/equal.rb21
-rw-r--r--spec/ruby/core/float/shared/to_i.rb4
-rw-r--r--spec/ruby/core/hash/hash_spec.rb9
-rw-r--r--spec/ruby/core/io/fixtures/classes.rb12
-rw-r--r--spec/ruby/core/io/gets_spec.rb6
-rw-r--r--spec/ruby/core/io/lineno_spec.rb9
-rw-r--r--spec/ruby/core/io/new_spec.rb2
-rw-r--r--spec/ruby/core/io/path_spec.rb14
-rw-r--r--spec/ruby/core/io/pipe_spec.rb11
-rw-r--r--spec/ruby/core/io/print_spec.rb25
-rw-r--r--spec/ruby/core/io/read_nonblock_spec.rb49
-rw-r--r--spec/ruby/core/io/read_spec.rb17
-rw-r--r--spec/ruby/core/io/readchar_spec.rb66
-rw-r--r--spec/ruby/core/io/readline_spec.rb4
-rw-r--r--spec/ruby/core/io/readlines_spec.rb4
-rw-r--r--spec/ruby/core/io/readpartial_spec.rb17
-rw-r--r--spec/ruby/core/io/rewind_spec.rb15
-rw-r--r--spec/ruby/core/io/set_encoding_by_bom_spec.rb187
-rw-r--r--spec/ruby/core/io/set_encoding_spec.rb49
-rw-r--r--spec/ruby/core/io/shared/each.rb26
-rw-r--r--spec/ruby/core/io/shared/new.rb2
-rw-r--r--spec/ruby/core/io/shared/pos.rb8
-rw-r--r--spec/ruby/core/io/shared/readlines.rb4
-rw-r--r--spec/ruby/core/io/shared/write.rb10
-rw-r--r--spec/ruby/core/io/sysread_spec.rb31
-rw-r--r--spec/ruby/core/io/sysseek_spec.rb2
-rw-r--r--spec/ruby/core/io/syswrite_spec.rb10
-rw-r--r--spec/ruby/core/io/write_nonblock_spec.rb10
-rw-r--r--spec/ruby/core/io/write_spec.rb10
-rw-r--r--spec/ruby/core/kernel/Complex_spec.rb89
-rw-r--r--spec/ruby/core/kernel/fixtures/Complex.rb5
-rw-r--r--spec/ruby/core/kernel/p_spec.rb6
-rw-r--r--spec/ruby/core/kernel/shared/load.rb45
-rw-r--r--spec/ruby/core/kernel/shared/require.rb11
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb65
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb33
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb2
-rw-r--r--spec/ruby/core/main/fixtures/using.rb1
-rw-r--r--spec/ruby/core/main/fixtures/using_in_main.rb5
-rw-r--r--spec/ruby/core/main/fixtures/using_in_method.rb5
-rw-r--r--spec/ruby/core/main/using_spec.rb20
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb45
-rw-r--r--spec/ruby/core/marshal/fixtures/classes.rb4
-rw-r--r--spec/ruby/core/marshal/shared/load.rb11
-rw-r--r--spec/ruby/core/matchdata/element_reference_spec.rb15
-rw-r--r--spec/ruby/core/matchdata/values_at_spec.rb73
-rw-r--r--spec/ruby/core/method/fixtures/classes.rb30
-rw-r--r--spec/ruby/core/method/owner_spec.rb6
-rw-r--r--spec/ruby/core/method/private_spec.rb21
-rw-r--r--spec/ruby/core/method/protected_spec.rb21
-rw-r--r--spec/ruby/core/method/public_spec.rb21
-rw-r--r--spec/ruby/core/method/super_method_spec.rb21
-rw-r--r--spec/ruby/core/method/unbind_spec.rb12
-rw-r--r--spec/ruby/core/module/const_defined_spec.rb7
-rw-r--r--spec/ruby/core/module/define_method_spec.rb43
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb9
-rw-r--r--spec/ruby/core/module/include_spec.rb4
-rw-r--r--spec/ruby/core/module/included_modules_spec.rb2
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb42
-rw-r--r--spec/ruby/core/module/prepend_spec.rb12
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb21
-rw-r--r--spec/ruby/core/objectspace/define_finalizer_spec.rb22
-rw-r--r--spec/ruby/core/process/_fork_spec.rb24
-rw-r--r--spec/ruby/core/process/constants_spec.rb1
-rw-r--r--spec/ruby/core/process/daemon_spec.rb3
-rw-r--r--spec/ruby/core/process/detach_spec.rb29
-rw-r--r--spec/ruby/core/process/spawn_spec.rb28
-rw-r--r--spec/ruby/core/process/times_spec.rb14
-rw-r--r--spec/ruby/core/queue/initialize_spec.rb13
-rw-r--r--spec/ruby/core/range/case_compare_spec.rb6
-rw-r--r--spec/ruby/core/range/last_spec.rb6
-rw-r--r--spec/ruby/core/range/size_spec.rb27
-rw-r--r--spec/ruby/core/refinement/import_methods_spec.rb34
-rw-r--r--spec/ruby/core/refinement/include_spec.rb27
-rw-r--r--spec/ruby/core/refinement/prepend_spec.rb27
-rw-r--r--spec/ruby/core/regexp/compile_spec.rb4
-rw-r--r--spec/ruby/core/regexp/initialize_spec.rb2
-rw-r--r--spec/ruby/core/regexp/new_spec.rb14
-rw-r--r--spec/ruby/core/regexp/shared/new.rb118
-rw-r--r--spec/ruby/core/regexp/timeout_spec.rb35
-rw-r--r--spec/ruby/core/signal/trap_spec.rb12
-rw-r--r--spec/ruby/core/string/byteindex_spec.rb16
-rw-r--r--spec/ruby/core/string/byteslice_spec.rb6
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb4
-rw-r--r--spec/ruby/core/string/chars_spec.rb7
-rw-r--r--spec/ruby/core/string/chomp_spec.rb4
-rw-r--r--spec/ruby/core/string/chop_spec.rb4
-rw-r--r--spec/ruby/core/string/clone_spec.rb4
-rw-r--r--spec/ruby/core/string/delete_prefix_spec.rb4
-rw-r--r--spec/ruby/core/string/delete_spec.rb4
-rw-r--r--spec/ruby/core/string/delete_suffix_spec.rb4
-rw-r--r--spec/ruby/core/string/downcase_spec.rb4
-rw-r--r--spec/ruby/core/string/dump_spec.rb10
-rw-r--r--spec/ruby/core/string/dup_spec.rb4
-rw-r--r--spec/ruby/core/string/element_set_spec.rb4
-rw-r--r--spec/ruby/core/string/fixtures/to_c.rb5
-rw-r--r--spec/ruby/core/string/gsub_spec.rb35
-rw-r--r--spec/ruby/core/string/index_spec.rb8
-rw-r--r--spec/ruby/core/string/lines_spec.rb1
-rw-r--r--spec/ruby/core/string/partition_spec.rb22
-rw-r--r--spec/ruby/core/string/reverse_spec.rb4
-rw-r--r--spec/ruby/core/string/rindex_spec.rb17
-rw-r--r--spec/ruby/core/string/rpartition_spec.rb22
-rw-r--r--spec/ruby/core/string/rstrip_spec.rb28
-rw-r--r--spec/ruby/core/string/scan_spec.rb6
-rw-r--r--spec/ruby/core/string/scrub_spec.rb10
-rw-r--r--spec/ruby/core/string/shared/each_line.rb6
-rw-r--r--spec/ruby/core/string/shared/partition.rb15
-rw-r--r--spec/ruby/core/string/shared/slice.rb13
-rw-r--r--spec/ruby/core/string/shared/strip.rb4
-rw-r--r--spec/ruby/core/string/shared/succ.rb4
-rw-r--r--spec/ruby/core/string/split_spec.rb43
-rw-r--r--spec/ruby/core/string/squeeze_spec.rb5
-rw-r--r--spec/ruby/core/string/sub_spec.rb32
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb4
-rw-r--r--spec/ruby/core/string/to_c_spec.rb108
-rw-r--r--spec/ruby/core/string/undump_spec.rb2
-rw-r--r--spec/ruby/core/string/unpack/b_spec.rb34
-rw-r--r--spec/ruby/core/string/unpack/c_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack/h_spec.rb28
-rw-r--r--spec/ruby/core/string/unpack/m_spec.rb5
-rw-r--r--spec/ruby/core/string/unpack/shared/basic.rb28
-rw-r--r--spec/ruby/core/string/unpack/shared/float.rb60
-rw-r--r--spec/ruby/core/string/unpack/shared/integer.rb88
-rw-r--r--spec/ruby/core/string/unpack/shared/unicode.rb14
-rw-r--r--spec/ruby/core/string/unpack/w_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack1_spec.rb12
-rw-r--r--spec/ruby/core/string/unpack_spec.rb34
-rw-r--r--spec/ruby/core/string/upcase_spec.rb4
-rw-r--r--spec/ruby/core/string/valid_encoding/utf_8_spec.rb214
-rw-r--r--spec/ruby/core/struct/initialize_spec.rb8
-rw-r--r--spec/ruby/core/struct/keyword_init_spec.rb21
-rw-r--r--spec/ruby/core/struct/values_at_spec.rb55
-rw-r--r--spec/ruby/core/symbol/shared/id2name.rb7
-rw-r--r--spec/ruby/core/symbol/to_proc_spec.rb8
-rw-r--r--spec/ruby/core/thread/backtrace/limit_spec.rb15
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb9
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb11
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb1
-rw-r--r--spec/ruby/core/thread/native_thread_id_spec.rb17
-rw-r--r--spec/ruby/core/time/at_spec.rb21
-rw-r--r--spec/ruby/core/time/localtime_spec.rb16
-rw-r--r--spec/ruby/core/time/new_spec.rb145
-rw-r--r--spec/ruby/core/time/now_spec.rb45
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb4
-rw-r--r--spec/ruby/core/time/shared/local.rb13
-rw-r--r--spec/ruby/core/time/shared/time_params.rb11
-rw-r--r--spec/ruby/core/time/strftime_spec.rb41
-rw-r--r--spec/ruby/core/time/utc_spec.rb41
-rw-r--r--spec/ruby/core/time/zone_spec.rb20
-rw-r--r--spec/ruby/core/tracepoint/allow_reentry_spec.rb32
-rw-r--r--spec/ruby/core/tracepoint/inspect_spec.rb9
-rw-r--r--spec/ruby/core/unboundmethod/equal_value_spec.rb39
-rw-r--r--spec/ruby/core/unboundmethod/fixtures/classes.rb16
-rw-r--r--spec/ruby/core/unboundmethod/hash_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/owner_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/private_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/protected_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/public_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/shared/to_s.rb16
-rw-r--r--spec/ruby/core/unboundmethod/super_method_spec.rb23
-rw-r--r--spec/ruby/fixtures/code/concurrent_require_fixture.rb4
-rw-r--r--spec/ruby/fixtures/code/load_wrap_fixture.rb (renamed from spec/ruby/fixtures/code/wrap_fixture.rb)3
-rw-r--r--spec/ruby/fixtures/constants.rb11
-rw-r--r--spec/ruby/language/block_spec.rb117
-rw-r--r--spec/ruby/language/case_spec.rb4
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb33
-rw-r--r--spec/ruby/language/method_spec.rb36
-rw-r--r--spec/ruby/language/precedence_spec.rb78
-rw-r--r--spec/ruby/language/predefined_spec.rb308
-rw-r--r--spec/ruby/language/proc_spec.rb7
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb5
-rw-r--r--spec/ruby/language/return_spec.rb15
-rw-r--r--spec/ruby/library/bigdecimal/exponent_spec.rb11
-rw-r--r--spec/ruby/library/bigdecimal/round_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/to_r_spec.rb12
-rw-r--r--spec/ruby/library/cgi/cookie/name_spec.rb12
-rw-r--r--spec/ruby/library/cgi/cookie/parse_spec.rb10
-rw-r--r--spec/ruby/library/cmath/math/acos_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/acosh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/asin_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/asinh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atan2_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atan_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/atanh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/cos_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/cosh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/exp_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/cmath/math/log10_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/log_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/shared/acos.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/acosh.rb37
-rw-r--r--spec/ruby/library/cmath/math/shared/asin.rb47
-rw-r--r--spec/ruby/library/cmath/math/shared/asinh.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan2.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/atanh.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cos.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cosh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/exp.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/log.rb39
-rw-r--r--spec/ruby/library/cmath/math/shared/log10.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/sin.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/sinh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/sqrt.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/tan.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/tanh.rb32
-rw-r--r--spec/ruby/library/cmath/math/sin_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/sinh_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/sqrt_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/tan_spec.rb1
-rw-r--r--spec/ruby/library/cmath/math/tanh_spec.rb1
-rw-r--r--spec/ruby/library/coverage/result_spec.rb54
-rw-r--r--spec/ruby/library/coverage/running_spec.rb20
-rw-r--r--spec/ruby/library/coverage/start_spec.rb8
-rw-r--r--spec/ruby/library/date/civil_spec.rb7
-rw-r--r--spec/ruby/library/datetime/to_time_spec.rb3
-rw-r--r--spec/ruby/library/erb/new_spec.rb16
-rw-r--r--spec/ruby/library/fiddle/handle/initialize_spec.rb10
-rw-r--r--spec/ruby/library/io-wait/wait_readable_spec.rb27
-rw-r--r--spec/ruby/library/io-wait/wait_writable_spec.rb20
-rw-r--r--spec/ruby/library/objectspace/fixtures/trace.rb5
-rw-r--r--spec/ruby/library/objectspace/trace_spec.rb15
-rw-r--r--spec/ruby/library/openssl/x509/name/verify_spec.rb4
-rw-r--r--spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb4
-rw-r--r--spec/ruby/library/rbconfig/unicode_version_spec.rb4
-rw-r--r--spec/ruby/library/scanf/io/block_scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/io/fixtures/date.txt4
-rw-r--r--spec/ruby/library/scanf/io/fixtures/helloworld.txt1
-rw-r--r--spec/ruby/library/scanf/io/scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/io/shared/block_scanf.rb28
-rw-r--r--spec/ruby/library/scanf/string/block_scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/string/scanf_spec.rb1
-rw-r--r--spec/ruby/library/scanf/string/shared/block_scanf.rb25
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb2
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb7
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb10
-rw-r--r--spec/ruby/library/stringio/initialize_spec.rb85
-rw-r--r--spec/ruby/library/stringio/new_spec.rb8
-rw-r--r--spec/ruby/library/stringio/open_spec.rb12
-rw-r--r--spec/ruby/library/stringio/printf_spec.rb27
-rw-r--r--spec/ruby/library/stringio/putc_spec.rb15
-rw-r--r--spec/ruby/library/stringio/puts_spec.rb14
-rw-r--r--spec/ruby/library/stringio/read_nonblock_spec.rb11
-rw-r--r--spec/ruby/library/stringio/shared/write.rb37
-rw-r--r--spec/ruby/library/stringio/write_nonblock_spec.rb6
-rw-r--r--spec/ruby/library/time/to_datetime_spec.rb3
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb5
-rw-r--r--spec/ruby/library/zlib/inflate/inflate_spec.rb7
-rw-r--r--spec/ruby/optional/capi/class_spec.rb31
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb42
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c22
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c35
-rw-r--r--spec/ruby/optional/capi/ext/globals_spec.c34
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c43
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c13
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h30
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c21
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c21
-rw-r--r--spec/ruby/optional/capi/fixtures/object.rb29
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb30
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb54
-rw-r--r--spec/ruby/optional/capi/io_spec.rb15
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb19
-rw-r--r--spec/ruby/optional/capi/object_spec.rb20
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb1
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb2
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb3
-rw-r--r--spec/ruby/optional/capi/string_spec.rb103
-rw-r--r--spec/ruby/optional/capi/util_spec.rb5
-rw-r--r--spec/ruby/security/cve_2019_8325_spec.rb15
-rw-r--r--spec/ruby/shared/kernel/complex.rb133
-rw-r--r--spec/ruby/shared/queue/deque.rb7
-rw-r--r--spec/ruby/shared/rational/Rational.rb48
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb8
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb9
-rw-r--r--spec/ruby/spec_helper.rb3
-rw-r--r--spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt74
-rwxr-xr-xspec/syntax_suggest/fixtures/rexe.rb.txt569
-rw-r--r--spec/syntax_suggest/fixtures/routes.rb.txt121
-rw-r--r--spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt1344
-rw-r--r--spec/syntax_suggest/fixtures/syntax_tree.rb.txt9234
-rw-r--r--spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt64
-rw-r--r--spec/syntax_suggest/fixtures/webmock.rb.txt35
-rw-r--r--spec/syntax_suggest/integration/exe_cli_spec.rb27
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb193
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb239
-rw-r--r--spec/syntax_suggest/spec_helper.rb104
-rw-r--r--spec/syntax_suggest/unit/api_spec.rb108
-rw-r--r--spec/syntax_suggest/unit/around_block_scan_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb230
-rw-r--r--spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb47
-rw-r--r--spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb44
-rw-r--r--spec/syntax_suggest/unit/capture_code_context_spec.rb229
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb260
-rw-r--r--spec/syntax_suggest/unit/cli_spec.rb224
-rw-r--r--spec/syntax_suggest/unit/code_block_spec.rb77
-rw-r--r--spec/syntax_suggest/unit/code_frontier_spec.rb135
-rw-r--r--spec/syntax_suggest/unit/code_line_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/code_search_spec.rb505
-rw-r--r--spec/syntax_suggest/unit/core_ext_spec.rb34
-rw-r--r--spec/syntax_suggest/unit/display_invalid_blocks_spec.rb174
-rw-r--r--spec/syntax_suggest/unit/explain_syntax_spec.rb255
-rw-r--r--spec/syntax_suggest/unit/lex_all_spec.rb29
-rw-r--r--spec/syntax_suggest/unit/pathname_from_message_spec.rb56
-rw-r--r--spec/syntax_suggest/unit/priority_queue_spec.rb95
-rw-r--r--spec/syntax_suggest/unit/scan_history_spec.rb114
-rw-r--r--sprintf.c78
-rw-r--r--st.c86
-rw-r--r--strftime.c7
-rw-r--r--string.c496
-rw-r--r--struct.c670
-rw-r--r--symbol.c76
-rw-r--r--symbol.rb15
-rw-r--r--template/Doxyfile.tmpl1
-rw-r--r--template/GNUmakefile.in15
-rw-r--r--template/Makefile.in37
-rw-r--r--template/extinit.c.tmpl2
-rw-r--r--template/fake.rb.in27
-rw-r--r--template/id.c.tmpl5
-rw-r--r--template/id.h.tmpl10
-rw-r--r--template/prelude.c.tmpl79
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb2
-rw-r--r--test/-ext-/eval/test_eval.rb4
-rw-r--r--test/-ext-/string/test_cstr.rb6
-rw-r--r--test/-ext-/string/test_fstring.rb8
-rw-r--r--test/-ext-/string/test_set_len.rb29
-rw-r--r--test/-ext-/symbol/test_type.rb5
-rw-r--r--test/-ext-/test_random.rb26
-rw-r--r--test/-ext-/thread_fd/test_thread_fd_close.rb1
-rw-r--r--test/bigdecimal/test_bigdecimal.rb14
-rw-r--r--test/cgi/test_cgi_cookie.rb85
-rw-r--r--test/cgi/test_cgi_header.rb8
-rw-r--r--test/cgi/test_cgi_util.rb18
-rw-r--r--test/coverage/autostart.rb2
-rw-r--r--test/coverage/main.rb1
-rw-r--r--test/coverage/test_coverage.rb66
-rw-r--r--test/csv/interface/test_read.rb18
-rw-r--r--test/csv/interface/test_write.rb9
-rw-r--r--test/csv/parse/test_convert.rb55
-rw-r--r--test/csv/parse/test_general.rb82
-rw-r--r--test/csv/parse/test_header.rb9
-rw-r--r--test/csv/parse/test_inputs_scanner.rb63
-rw-r--r--test/csv/parse/test_liberal_parsing.rb11
-rw-r--r--test/csv/parse/test_read.rb27
-rw-r--r--test/csv/test_data_converters.rb84
-rw-r--r--test/csv/test_encodings.rb31
-rw-r--r--test/csv/test_patterns.rb27
-rw-r--r--test/csv/test_table.rb73
-rw-r--r--test/date/test_date.rb29
-rw-r--r--test/date/test_date_parse.rb3
-rw-r--r--test/date/test_date_strptime.rb9
-rw-r--r--test/did_you_mean/spell_checking/test_method_name_check.rb18
-rw-r--r--test/did_you_mean/spell_checking/test_variable_name_check.rb12
-rw-r--r--test/drb/drbtest.rb2
-rw-r--r--test/drb/test_drb.rb5
-rw-r--r--test/drb/test_drbssl.rb8
-rw-r--r--test/erb/test_erb.rb30
-rw-r--r--test/erb/test_erb_command.rb18
-rw-r--r--test/error_highlight/test_error_highlight.rb62
-rw-r--r--test/excludes/Psych/TestDateTime.rb4
-rw-r--r--test/fiber/scheduler.rb97
-rw-r--r--test/fiber/test_address_resolve.rb2
-rw-r--r--test/fiber/test_enumerator.rb14
-rw-r--r--test/fiber/test_io.rb73
-rw-r--r--test/fiber/test_io_buffer.rb33
-rw-r--r--test/fiber/test_mutex.rb22
-rw-r--r--test/fiber/test_process.rb21
-rw-r--r--test/fiber/test_queue.rb54
-rw-r--r--test/fiber/test_scheduler.rb97
-rw-r--r--test/fiber/test_storage.rb115
-rw-r--r--test/fiber/test_thread.rb22
-rw-r--r--test/fiddle/helper.rb10
-rw-r--r--test/fiddle/test_closure.rb111
-rw-r--r--test/fiddle/test_fiddle.rb41
-rw-r--r--test/fiddle/test_func.rb36
-rw-r--r--test/fiddle/test_function.rb28
-rw-r--r--test/fiddle/test_handle.rb7
-rw-r--r--test/fiddle/test_import.rb23
-rw-r--r--test/fiddle/test_pack.rb37
-rw-r--r--test/fiddle/test_pointer.rb3
-rw-r--r--test/fileutils/test_fileutils.rb61
-rw-r--r--test/io/console/test_io_console.rb33
-rw-r--r--test/io/wait/test_io_wait.rb8
-rw-r--r--test/io/wait/test_io_wait_uncommon.rb1
-rw-r--r--test/io/wait/test_ractor.rb1
-rw-r--r--test/irb/helper.rb76
-rw-r--r--test/irb/test_cmd.rb862
-rw-r--r--test/irb/test_color.rb84
-rw-r--r--test/irb/test_color_printer.rb5
-rw-r--r--test/irb/test_completion.rb359
-rw-r--r--test/irb/test_context.rb47
-rw-r--r--test/irb/test_debug_cmd.rb303
-rw-r--r--test/irb/test_history.rb68
-rw-r--r--test/irb/test_init.rb81
-rw-r--r--test/irb/test_input_method.rb79
-rw-r--r--test/irb/test_option.rb4
-rw-r--r--test/irb/test_raise_no_backtrace_exception.rb10
-rw-r--r--test/irb/test_ruby_lex.rb130
-rw-r--r--test/irb/test_workspace.rb5
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb23
-rw-r--r--test/lib/jit_support.rb35
-rw-r--r--test/mkmf/base.rb1
-rw-r--r--test/mkmf/test_config.rb4
-rw-r--r--test/mkmf/test_constant.rb8
-rw-r--r--test/mkmf/test_pkg_config.rb4
-rw-r--r--test/net/fixtures/Makefile6
-rw-r--r--test/net/fixtures/cacert.pem44
-rw-r--r--test/net/fixtures/server.crt99
-rw-r--r--test/net/fixtures/server.key55
-rw-r--r--test/net/http/test_http.rb34
-rw-r--r--test/net/http/test_httpheader.rb6
-rw-r--r--test/net/http/test_httpresponse.rb39
-rw-r--r--test/net/http/test_https.rb8
-rw-r--r--test/net/protocol/test_protocol.rb37
-rw-r--r--test/objspace/test_objspace.rb140
-rw-r--r--test/objspace/test_ractor.rb17
-rw-r--r--test/open-uri/test_open-uri.rb11
-rw-r--r--test/open-uri/test_ssl.rb19
-rw-r--r--test/openssl/fixtures/pkey/p256_too_large.pem5
-rw-r--r--test/openssl/fixtures/pkey/p384_invalid.pem6
-rw-r--r--test/openssl/test_asn1.rb22
-rw-r--r--test/openssl/test_bn.rb6
-rw-r--r--test/openssl/test_cipher.rb6
-rw-r--r--test/openssl/test_hmac.rb9
-rw-r--r--test/openssl/test_ns_spki.rb2
-rw-r--r--test/openssl/test_pair.rb2
-rw-r--r--test/openssl/test_pkey.rb5
-rw-r--r--test/openssl/test_pkey_dsa.rb23
-rw-r--r--test/openssl/test_pkey_ec.rb36
-rw-r--r--test/openssl/test_pkey_rsa.rb23
-rw-r--r--test/openssl/test_ssl.rb87
-rw-r--r--test/openssl/test_ssl_session.rb2
-rw-r--r--test/openssl/test_x509cert.rb4
-rw-r--r--test/openssl/test_x509crl.rb20
-rw-r--r--test/openssl/test_x509req.rb30
-rw-r--r--test/optparse/test_load.rb141
-rw-r--r--test/optparse/test_optparse.rb15
-rw-r--r--test/optparse/test_summary.rb23
-rw-r--r--test/psych/test_coder.rb6
-rw-r--r--test/psych/test_date_time.rb20
-rw-r--r--test/psych/test_encoding.rb7
-rw-r--r--test/psych/test_parser.rb8
-rw-r--r--test/psych/test_yaml.rb2
-rw-r--r--test/racc/case.rb2
-rw-r--r--test/rdoc/support/test_case.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_darkfish.rb70
-rw-r--r--test/rdoc/test_rdoc_generator_json_index.rb14
-rw-r--r--test/rdoc/test_rdoc_markdown.rb21
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb80
-rw-r--r--test/rdoc/test_rdoc_options.rb11
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb8
-rw-r--r--test/rdoc/test_rdoc_rd_block_parser.rb21
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb42
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb17
-rw-r--r--test/rdoc/test_rdoc_store.rb3
-rw-r--r--test/readline/test_readline.rb7
-rw-r--r--test/reline/test_config.rb14
-rw-r--r--test/reline/test_key_actor_vi.rb8
-rw-r--r--test/reline/test_reline.rb35
-rw-r--r--test/reline/yamatanooroti/termination_checker.rb4
-rw-r--r--test/resolv/test_dns.rb7
-rw-r--r--test/rinda/test_rinda.rb2
-rw-r--r--test/ripper/test_lexer.rb72
-rw-r--r--test/ripper/test_parser_events.rb17
-rw-r--r--test/ripper/test_ripper.rb22
-rw-r--r--test/ripper/test_scanner_events.rb13
-rw-r--r--test/ruby/enc/test_cesu8.rb4
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb3
-rw-r--r--test/ruby/test_argf.rb1
-rw-r--r--test/ruby/test_array.rb32
-rw-r--r--test/ruby/test_assignment.rb10
-rw-r--r--test/ruby/test_ast.rb497
-rw-r--r--test/ruby/test_autoload.rb18
-rw-r--r--test/ruby/test_call.rb7
-rw-r--r--test/ruby/test_class.rb41
-rw-r--r--test/ruby/test_clone.rb7
-rw-r--r--test/ruby/test_complex.rb117
-rw-r--r--test/ruby/test_data.rb249
-rw-r--r--test/ruby/test_dir.rb67
-rw-r--r--test/ruby/test_encoding.rb21
-rw-r--r--test/ruby/test_enumerator.rb89
-rw-r--r--test/ruby/test_env.rb16
-rw-r--r--test/ruby/test_exception.rb73
-rw-r--r--test/ruby/test_file.rb2
-rw-r--r--test/ruby/test_file_exhaustive.rb2
-rw-r--r--test/ruby/test_float.rb14
-rw-r--r--test/ruby/test_gc.rb54
-rw-r--r--test/ruby/test_gc_compact.rb208
-rw-r--r--test/ruby/test_hash.rb71
-rw-r--r--test/ruby/test_integer.rb33
-rw-r--r--test/ruby/test_io.rb127
-rw-r--r--test/ruby/test_io_buffer.rb244
-rw-r--r--test/ruby/test_io_timeout.rb58
-rw-r--r--test/ruby/test_iseq.rb52
-rw-r--r--test/ruby/test_keyword.rb214
-rw-r--r--test/ruby/test_lazy_enumerator.rb20
-rw-r--r--test/ruby/test_m17n.rb42
-rw-r--r--test/ruby/test_marshal.rb8
-rw-r--r--test/ruby/test_method.rb136
-rw-r--r--test/ruby/test_mjit.rb334
-rw-r--r--test/ruby/test_module.rb89
-rw-r--r--test/ruby/test_numeric.rb3
-rw-r--r--test/ruby/test_object.rb31
-rw-r--r--test/ruby/test_objectspace.rb2
-rw-r--r--test/ruby/test_optimization.rb14
-rw-r--r--test/ruby/test_pack.rb60
-rw-r--r--test/ruby/test_parse.rb22
-rw-r--r--test/ruby/test_pattern_matching.rb14
-rw-r--r--test/ruby/test_proc.rb44
-rw-r--r--test/ruby/test_process.rb157
-rw-r--r--test/ruby/test_rand.rb8
-rw-r--r--test/ruby/test_range.rb79
-rw-r--r--test/ruby/test_rational.rb2
-rw-r--r--test/ruby/test_regexp.rb294
-rw-r--r--test/ruby/test_require.rb67
-rw-r--r--test/ruby/test_rubyoptions.rb26
-rw-r--r--test/ruby/test_rubyvm.rb2
-rw-r--r--test/ruby/test_rubyvm_mjit.rb23
-rw-r--r--test/ruby/test_settracefunc.rb170
-rw-r--r--test/ruby/test_shapes.rb441
-rw-r--r--test/ruby/test_sprintf.rb15
-rw-r--r--test/ruby/test_string.rb54
-rw-r--r--test/ruby/test_struct.rb14
-rw-r--r--test/ruby/test_super.rb29
-rw-r--r--test/ruby/test_syntax.rb50
-rw-r--r--test/ruby/test_thread.rb9
-rw-r--r--test/ruby/test_time.rb123
-rw-r--r--test/ruby/test_time_tz.rb36
-rw-r--r--test/ruby/test_transcode.rb12
-rw-r--r--test/ruby/test_variable.rb25
-rw-r--r--test/ruby/test_vm_dump.rb2
-rw-r--r--test/ruby/test_weakmap.rb46
-rw-r--r--test/ruby/test_yjit.rb443
-rw-r--r--test/ruby/test_yjit_exit_locations.rb4
-rw-r--r--test/rubygems/alternate_cert.pem28
-rw-r--r--test/rubygems/alternate_cert_32.pem30
-rw-r--r--test/rubygems/alternate_key.pem50
-rw-r--r--test/rubygems/bad_rake.rb1
-rw-r--r--test/rubygems/bundler_test_gem.rb421
-rw-r--r--test/rubygems/child_cert.pem31
-rw-r--r--test/rubygems/child_cert_32.pem31
-rw-r--r--test/rubygems/child_key.pem50
-rw-r--r--test/rubygems/encrypted_private_key.pem52
-rw-r--r--test/rubygems/expired_cert.pem30
-rw-r--r--test/rubygems/fake_certlib/openssl.rb1
-rw-r--r--test/rubygems/future_cert.pem30
-rw-r--r--test/rubygems/future_cert_32.pem30
-rw-r--r--test/rubygems/good_rake.rb1
-rw-r--r--test/rubygems/grandchild_cert.pem31
-rw-r--r--test/rubygems/grandchild_cert_32.pem31
-rw-r--r--test/rubygems/grandchild_key.pem50
-rw-r--r--test/rubygems/helper.rb69
-rw-r--r--test/rubygems/installer_test_case.rb1
-rw-r--r--test/rubygems/invalid_issuer_cert.pem32
-rw-r--r--test/rubygems/invalid_issuer_cert_32.pem32
-rw-r--r--test/rubygems/invalid_key.pem50
-rw-r--r--test/rubygems/invalid_signer_cert.pem30
-rw-r--r--test/rubygems/invalid_signer_cert_32.pem30
-rw-r--r--test/rubygems/invalidchild_cert.pem31
-rw-r--r--test/rubygems/invalidchild_cert_32.pem31
-rw-r--r--test/rubygems/invalidchild_key.pem50
-rw-r--r--test/rubygems/multifactor_auth_utilities.rb111
-rw-r--r--test/rubygems/package/tar_test_case.rb66
-rw-r--r--test/rubygems/packages/Bluebie-legs-0.6.2.gembin0 -> 14336 bytes-rw-r--r--test/rubygems/plugin/exception/rubygems_plugin.rb1
-rw-r--r--test/rubygems/plugin/load/rubygems_plugin.rb1
-rw-r--r--test/rubygems/plugin/standarderror/rubygems_plugin.rb1
-rw-r--r--test/rubygems/private_key.pem50
-rw-r--r--test/rubygems/public_cert.pem32
-rw-r--r--test/rubygems/public_cert_32.pem30
-rw-r--r--test/rubygems/public_key.pem14
-rw-r--r--test/rubygems/rubygems/commands/crash_command.rb1
-rw-r--r--test/rubygems/rubygems_plugin.rb1
-rw-r--r--test/rubygems/simple_gem.rb3
-rw-r--r--test/rubygems/specifications/bar-0.0.2.gemspec2
-rw-r--r--test/rubygems/specifications/rubyforge-0.0.1.gemspec2
-rw-r--r--test/rubygems/test_bundled_ca.rb3
-rw-r--r--test/rubygems/test_config.rb3
-rw-r--r--test/rubygems/test_deprecate.rb3
-rw-r--r--test/rubygems/test_exit.rb2
-rw-r--r--test/rubygems/test_gem.rb426
-rw-r--r--test/rubygems/test_gem_available_set.rb1
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb6
-rw-r--r--test/rubygems/test_gem_command.rb1
-rw-r--r--test/rubygems/test_gem_command_manager.rb68
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_check_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_exec_command.rb853
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb27
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb22
-rw-r--r--test/rubygems/test_gem_commands_list_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_mirror.rb1
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb235
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb51
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb179
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_search_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb15
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb32
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb46
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb13
-rw-r--r--test/rubygems/test_gem_commands_which_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb139
-rw-r--r--test/rubygems/test_gem_config_file.rb3
-rw-r--r--test/rubygems/test_gem_dependency.rb3
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb35
-rw-r--r--test/rubygems/test_gem_dependency_list.rb1
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb1
-rw-r--r--test/rubygems/test_gem_doctor.rb1
-rw-r--r--test/rubygems/test_gem_ext_builder.rb15
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder.rb77
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/build.rb21
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/custom_name.gemspec8
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock)56
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml)2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/src/lib.rs (renamed from test/rubygems/test_gem_ext_cargo_builder/custom_name/src/lib.rs)2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/lib/custom_name.rb3
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock70
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/build.rb21
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/rust_ruby_example.gemspec2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs12
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb33
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_unit.rb25
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb3
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb1
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb1
-rw-r--r--test/rubygems/test_gem_gem_runner.rb10
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb174
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb1
-rw-r--r--test/rubygems/test_gem_indexer.rb60
-rw-r--r--test/rubygems/test_gem_install_update_options.rb1
-rw-r--r--test/rubygems/test_gem_installer.rb125
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb1
-rw-r--r--test/rubygems/test_gem_name_tuple.rb1
-rw-r--r--test/rubygems/test_gem_package.rb40
-rw-r--r--test/rubygems/test_gem_package_old.rb1
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb27
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb50
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb158
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb1
-rw-r--r--test/rubygems/test_gem_package_task.rb1
-rw-r--r--test/rubygems/test_gem_path_support.rb1
-rw-r--r--test/rubygems/test_gem_platform.rb214
-rw-r--r--test/rubygems/test_gem_rdoc.rb1
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb11
-rw-r--r--test/rubygems/test_gem_request.rb14
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb1
-rw-r--r--test/rubygems/test_gem_request_set.rb5
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb21
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb1
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_parser.rb13
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_tokenizer.rb1
-rw-r--r--test/rubygems/test_gem_requirement.rb3
-rw-r--r--test/rubygems/test_gem_resolver.rb74
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb1
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb25
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb39
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb1
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb1
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb19
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb5
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_index_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_installed_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_local_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb1
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_vendor_specification.rb1
-rw-r--r--test/rubygems/test_gem_security.rb11
-rw-r--r--test/rubygems/test_gem_security_policy.rb20
-rw-r--r--test/rubygems/test_gem_security_signer.rb13
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb1
-rw-r--r--test/rubygems/test_gem_silent_ui.rb1
-rw-r--r--test/rubygems/test_gem_source.rb3
-rw-r--r--test/rubygems/test_gem_source_fetch_problem.rb1
-rw-r--r--test/rubygems/test_gem_source_git.rb31
-rw-r--r--test/rubygems/test_gem_source_installed.rb3
-rw-r--r--test/rubygems/test_gem_source_list.rb1
-rw-r--r--test/rubygems/test_gem_source_local.rb1
-rw-r--r--test/rubygems/test_gem_source_lock.rb7
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb1
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb1
-rw-r--r--test/rubygems/test_gem_source_vendor.rb3
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb1
-rw-r--r--test/rubygems/test_gem_specification.rb98
-rw-r--r--test/rubygems/test_gem_stream_ui.rb37
-rw-r--r--test/rubygems/test_gem_stub_specification.rb1
-rw-r--r--test/rubygems/test_gem_text.rb1
-rw-r--r--test/rubygems/test_gem_uninstaller.rb11
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb1
-rw-r--r--test/rubygems/test_gem_update_suggestion.rb209
-rw-r--r--test/rubygems/test_gem_uri.rb2
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb1
-rw-r--r--test/rubygems/test_gem_util.rb1
-rw-r--r--test/rubygems/test_gem_version.rb5
-rw-r--r--test/rubygems/test_gem_version_option.rb1
-rw-r--r--test/rubygems/test_kernel.rb13
-rw-r--r--test/rubygems/test_project_sanity.rb35
-rw-r--r--test/rubygems/test_remote_fetch_error.rb3
-rw-r--r--test/rubygems/test_require.rb135
-rw-r--r--test/rubygems/test_rubygems.rb2
-rw-r--r--test/rubygems/test_webauthn_listener.rb143
-rw-r--r--test/rubygems/test_webauthn_listener_response.rb93
-rw-r--r--test/rubygems/test_webauthn_poller.rb124
-rw-r--r--test/rubygems/utilities.rb94
-rw-r--r--test/rubygems/wrong_key_cert.pem30
-rw-r--r--test/rubygems/wrong_key_cert_32.pem30
-rw-r--r--test/runner.rb12
-rw-r--r--test/socket/test_addrinfo.rb4
-rw-r--r--test/socket/test_nonblock.rb4
-rw-r--r--test/socket/test_socket.rb2
-rw-r--r--test/socket/test_tcp.rb2
-rw-r--r--test/socket/test_unix.rb147
-rw-r--r--test/stringio/test_stringio.rb2
-rw-r--r--test/strscan/test_ractor.rb2
-rw-r--r--test/strscan/test_stringscanner.rb63
-rw-r--r--test/test_extlibs.rb2
-rw-r--r--test/test_pp.rb9
-rw-r--r--test/test_rbconfig.rb9
-rw-r--r--test/test_time.rb9
-rw-r--r--test/test_timeout.rb13
-rw-r--r--test/test_tmpdir.rb18
-rw-r--r--test/test_trick.rb23
-rw-r--r--test/uri/test_common.rb11
-rw-r--r--test/uri/test_generic.rb27
-rw-r--r--test/uri/test_ldap.rb6
-rw-r--r--test/uri/test_parser.rb29
-rw-r--r--test/yaml/test_store.rb2
-rw-r--r--test/zlib/test_zlib.rb4
-rw-r--r--thread.c112
-rw-r--r--thread_none.c7
-rw-r--r--thread_pthread.c3
-rw-r--r--thread_pthread.h35
-rw-r--r--thread_sync.c128
-rw-r--r--thread_sync.rb6
-rw-r--r--time.c1343
-rw-r--r--timev.h11
-rw-r--r--timev.rb275
-rw-r--r--tool/bundler/dev_gems.rb12
-rw-r--r--tool/bundler/dev_gems.rb.lock43
-rw-r--r--tool/bundler/rubocop_gems.rb1
-rw-r--r--tool/bundler/rubocop_gems.rb.lock60
-rw-r--r--tool/bundler/standard_gems.rb1
-rw-r--r--tool/bundler/standard_gems.rb.lock70
-rw-r--r--tool/bundler/test_gems.rb2
-rw-r--r--tool/bundler/test_gems.rb.lock11
-rwxr-xr-xtool/checksum.rb4
-rw-r--r--tool/downloader.rb84
-rwxr-xr-x[-rw-r--r--]tool/enc-case-folding.rb (renamed from enc/unicode/case-folding.rb)10
-rw-r--r--tool/enc-emoji-citrus-gen.rb4
-rwxr-xr-xtool/enc-unicode.rb31
-rwxr-xr-xtool/expand-config.rb14
-rw-r--r--tool/fake.rb9
-rwxr-xr-xtool/file2lastrev.rb91
-rwxr-xr-xtool/gen-mailmap.rb4
-rw-r--r--tool/generic_erb.rb44
-rwxr-xr-xtool/id2token.rb11
-rw-r--r--tool/lib/bundled_gem.rb13
-rw-r--r--tool/lib/colorize.rb4
-rw-r--r--tool/lib/core_assertions.rb76
-rw-r--r--tool/lib/envutil.rb18
-rw-r--r--tool/lib/leakchecker.rb6
-rw-r--r--tool/lib/output.rb57
-rw-r--r--tool/lib/test/unit.rb34
-rw-r--r--tool/lib/vcs.rb149
-rw-r--r--tool/lib/vpath.rb7
-rw-r--r--tool/lib/webrick/httpserver.rb1
-rw-r--r--tool/lib/webrick/httputils.rb2
-rwxr-xr-xtool/ln_sr.rb8
-rw-r--r--tool/m4/ruby_default_arch.m41
-rw-r--r--tool/m4/ruby_wasm_tools.m413
-rwxr-xr-xtool/make-snapshot38
-rw-r--r--tool/make_hgraph.rb7
-rwxr-xr-xtool/merger.rb189
-rwxr-xr-xtool/mjit/bindgen.rb435
-rw-r--r--tool/mk_builtin_loader.rb67
-rwxr-xr-xtool/mkconfig.rb12
-rwxr-xr-xtool/mkrunnable.rb4
-rwxr-xr-xtool/outdate-bundled-gems.rb135
-rwxr-xr-xtool/pure_parser.rb24
-rwxr-xr-xtool/rbinstall.rb89
-rw-r--r--tool/rbs_skip_tests11
-rwxr-xr-xtool/redmine-backporter.rb158
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb5
-rw-r--r--tool/ruby_vm/helpers/dumper.rb7
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb12
-rw-r--r--tool/ruby_vm/views/_mjit_compile_getinlinecache.erb31
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn.erb92
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn_body.erb129
-rw-r--r--tool/ruby_vm/views/_mjit_compile_invokebuiltin.erb29
-rw-r--r--tool/ruby_vm/views/_mjit_compile_ivar.erb110
-rw-r--r--tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb38
-rw-r--r--tool/ruby_vm/views/_mjit_compile_send.erb119
-rw-r--r--tool/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb40
-rw-r--r--tool/ruby_vm/views/mjit_compile.inc.erb110
-rw-r--r--tool/ruby_vm/views/mjit_sp_inc.inc.erb17
-rwxr-xr-xtool/runruby.rb9
-rwxr-xr-xtool/sync_default_gems.rb1264
-rw-r--r--tool/test-bundled-gems.rb37
-rwxr-xr-xtool/test/test_sync_default_gems.rb76
-rw-r--r--tool/test/webrick/test_filehandler.rb4
-rw-r--r--tool/test/webrick/test_httprequest.rb2
-rw-r--r--tool/transcode-tblgen.rb2
-rw-r--r--tool/update-NEWS-refs.rb37
-rwxr-xr-xtool/update-bundled_gems.rb3
-rwxr-xr-xtool/update-deps6
-rw-r--r--trace_point.rb51
-rw-r--r--transcode.c4
-rw-r--r--transient_heap.c3
-rw-r--r--util.c6
-rw-r--r--variable.c1204
-rw-r--r--variable.h10
-rw-r--r--version.c47
-rw-r--r--version.h47
-rw-r--r--vm.c228
-rw-r--r--vm_args.c9
-rw-r--r--vm_backtrace.c66
-rw-r--r--vm_callinfo.h82
-rw-r--r--vm_core.h140
-rw-r--r--vm_dump.c39
-rw-r--r--vm_eval.c36
-rw-r--r--vm_exec.c6
-rw-r--r--vm_insnhelper.c792
-rw-r--r--vm_insnhelper.h3
-rw-r--r--vm_method.c156
-rw-r--r--vm_trace.c61
-rw-r--r--wasm/README.md10
-rw-r--r--wasm/machine.c6
-rw-r--r--wasm/machine.h5
-rw-r--r--win32/Makefile.sub77
-rwxr-xr-xwin32/configure.bat7
-rw-r--r--win32/dir.h2
-rw-r--r--win32/file.c47
-rw-r--r--win32/file.h10
-rwxr-xr-xwin32/ifchange.bat1
-rwxr-xr-xwin32/mkexports.rb4
-rwxr-xr-xwin32/resource.rb2
-rw-r--r--win32/setup.mak16
-rw-r--r--win32/win32.c258
-rw-r--r--win32/winmain.c4
-rw-r--r--yjit.c199
-rw-r--r--yjit.h12
-rw-r--r--yjit.rb128
-rw-r--r--yjit/Cargo.lock7
-rw-r--r--yjit/Cargo.toml8
-rw-r--r--yjit/bindgen/Cargo.lock131
-rw-r--r--yjit/bindgen/Cargo.toml2
-rw-r--r--yjit/bindgen/src/main.rs113
-rw-r--r--yjit/not_gmake.mk14
-rw-r--r--yjit/src/asm/arm64/README.md16
-rw-r--r--yjit/src/asm/arm64/arg/bitmask_imm.rs255
-rw-r--r--yjit/src/asm/arm64/arg/condition.rs52
-rw-r--r--yjit/src/asm/arm64/arg/inst_offset.rs47
-rw-r--r--yjit/src/asm/arm64/arg/mod.rs18
-rw-r--r--yjit/src/asm/arm64/arg/sf.rs19
-rw-r--r--yjit/src/asm/arm64/arg/shifted_imm.rs81
-rw-r--r--yjit/src/asm/arm64/arg/sys_reg.rs6
-rw-r--r--yjit/src/asm/arm64/arg/truncate.rs66
-rw-r--r--yjit/src/asm/arm64/inst/atomic.rs86
-rw-r--r--yjit/src/asm/arm64/inst/branch.rs100
-rw-r--r--yjit/src/asm/arm64/inst/branch_cond.rs78
-rw-r--r--yjit/src/asm/arm64/inst/breakpoint.rs55
-rw-r--r--yjit/src/asm/arm64/inst/call.rs104
-rw-r--r--yjit/src/asm/arm64/inst/conditional.rs73
-rw-r--r--yjit/src/asm/arm64/inst/data_imm.rs143
-rw-r--r--yjit/src/asm/arm64/inst/data_reg.rs192
-rw-r--r--yjit/src/asm/arm64/inst/halfword_imm.rs179
-rw-r--r--yjit/src/asm/arm64/inst/load_literal.rs89
-rw-r--r--yjit/src/asm/arm64/inst/load_register.rs108
-rw-r--r--yjit/src/asm/arm64/inst/load_store.rs249
-rw-r--r--yjit/src/asm/arm64/inst/load_store_exclusive.rs109
-rw-r--r--yjit/src/asm/arm64/inst/logical_imm.rs154
-rw-r--r--yjit/src/asm/arm64/inst/logical_reg.rs207
-rw-r--r--yjit/src/asm/arm64/inst/mod.rs50
-rw-r--r--yjit/src/asm/arm64/inst/mov.rs155
-rw-r--r--yjit/src/asm/arm64/inst/nop.rs44
-rw-r--r--yjit/src/asm/arm64/inst/pc_rel.rs107
-rw-r--r--yjit/src/asm/arm64/inst/reg_pair.rs212
-rw-r--r--yjit/src/asm/arm64/inst/sbfm.rs103
-rw-r--r--yjit/src/asm/arm64/inst/shift_imm.rs147
-rw-r--r--yjit/src/asm/arm64/inst/sys_reg.rs86
-rw-r--r--yjit/src/asm/arm64/inst/test_bit.rs133
-rw-r--r--yjit/src/asm/arm64/mod.rs1580
-rw-r--r--yjit/src/asm/arm64/opnd.rs195
-rw-r--r--yjit/src/asm/mod.rs626
-rw-r--r--yjit/src/asm/x86_64/mod.rs368
-rw-r--r--yjit/src/asm/x86_64/tests.rs29
-rw-r--r--yjit/src/backend/arm64/mod.rs1491
-rw-r--r--yjit/src/backend/ir.rs1576
-rw-r--r--yjit/src/backend/mod.rs8
-rw-r--r--yjit/src/backend/tests.rs331
-rw-r--r--yjit/src/backend/x86_64/mod.rs895
-rw-r--r--yjit/src/codegen.rs4983
-rw-r--r--yjit/src/core.rs889
-rw-r--r--yjit/src/cruby.rs354
-rw-r--r--yjit/src/cruby_bindings.inc.rs1247
-rw-r--r--yjit/src/disasm.rs163
-rw-r--r--yjit/src/invariants.rs251
-rw-r--r--yjit/src/lib.rs2
-rw-r--r--yjit/src/options.rs60
-rw-r--r--yjit/src/stats.rs123
-rw-r--r--yjit/src/utils.rs262
-rw-r--r--yjit/src/virtualmem.rs111
-rw-r--r--yjit/src/yjit.rs45
-rw-r--r--yjit/yjit.mk70
2093 files changed, 125658 insertions, 34952 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 1ea5e59210..05ff204541 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -17,18 +17,18 @@ skip_commits:
- '**/*.md'
- '**/*.rdoc'
- '**/.document'
+ - '**/*.[1-8]'
+ - '**/*.ronn'
environment:
ruby_version: "24-%Platform%"
- zlib_version: "1.2.12"
matrix:
+ # Test only the oldest supported version because AppVeyor is unstable, its concurrency
+ # is limited, and compatibility issues that happen only in newer versions are rare.
+ # You may test some other stuff on GitHub Actions instead.
- build: vs
- vs: 120
- ssl: OpenSSL
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- GEMS_FOR_TEST: ""
- - build: vs
- vs: 140
+ vs: 120 # Visual Studio 2013
ssl: OpenSSL-v111
+ # The worker image name. This is NOT the Visual Studio version we're using here.
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GEMS_FOR_TEST: ""
RELINE_TEST_ENCODING: "UTF-8"
@@ -48,8 +48,9 @@ for:
- cd C:\Tools\vcpkg
- git pull -q
- .\bootstrap-vcpkg.bat
+ - ps: Start-FileDownload 'https://github.com/microsoft/vcpkg-tool/releases/download/2023-08-09/vcpkg.exe' -FileName 'C:\Tools\vcpkg\vcpkg.exe'
- cd %APPVEYOR_BUILD_FOLDER%
- - vcpkg --triplet %Platform%-windows install libffi libyaml readline zlib
+ - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib
- CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- SET vcvars
- '"%vcvars%" %Platform:x64=amd64%'
@@ -69,9 +70,6 @@ for:
- mkdir \usr\local\bin
- mkdir \usr\local\include
- mkdir \usr\local\lib
- - SET ZLIB_ZIP=.downloaded-cache\zlib%zlib_version:.=%.zip
- - if not exist %ZLIB_ZIP% curl -fsSL -o %ZLIB_ZIP% --retry 10 https://zlib.net/zlib%zlib_version:.=%.zip
- - 7z x -aos -o%APPVEYOR_BUILD_FOLDER%\ext\zlib %ZLIB_ZIP%
- for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do (
if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I
@@ -79,6 +77,7 @@ for:
- attrib +r /s /d
- mkdir %Platform%-mswin_%vs%
build_script:
+ - set HAVE_GIT=no
- cd %APPVEYOR_BUILD_FOLDER%
- cd %Platform%-mswin_%vs%
- >-
@@ -95,7 +94,7 @@ for:
- nmake -l "TESTOPTS=-v -q" btest
- nmake -l "TESTOPTS=-v -q" test-basic
- >-
- nmake -l "TESTOPTS=-v --timeout-scale=3.0
+ nmake -l "TESTOPTS=--timeout-scale=3.0
--excludes=../test/excludes/_appveyor -j%JOBS%
--exclude win32ole
--exclude test_bignum
@@ -106,7 +105,7 @@ for:
# separately execute tests without -j which may crash worker with -j.
- >-
nmake -l
- "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
+ "TESTOPTS=--timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
TESTS="
../test/win32ole
../test/ruby/test_bignum.rb
@@ -124,7 +123,7 @@ notifications:
{{^isPullRequest}}
{
"ci": "AppVeyor CI",
- "env": "Visual Studio 2013 / 2015",
+ "env": "Visual Studio 2013",
"url": "{{buildUrl}}",
"commit": "{{commitId}}",
"branch": "{{branch}}"
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index ec8036297c..0000000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-# This CI is used to test Arm cases. We can set the maximum 16 tasks.
-# The entire testing design is inspired from .github/workflows/compilers.yml.
-
-# By default, Cirrus mounts an empty volume to `/tmp`
-# which triggers all sorts of warnings like "system temporary path is world-writable: /tmp".
-# Lets workaround it by specifying a custom volume mount point.
-env:
- CIRRUS_VOLUME: /cirrus-ci-volume
- LANG: C.UTF-8
-
-task:
- name: Arm64 Graviton2 / $CC
- skip: "changesIncludeOnly('doc/**', '**.{md,rdoc}', '.document')"
- arm_container:
- # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image .
- image: ghcr.io/ruby/ruby-ci-image:$CC
- # Define the used cpu core in each matrix task. We can use total 16 cpu
- # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks]
- cpu: 8
- # We can request maximum 4 GB per cpu.
- # [memory per task] = [memory per cpu: 4 GB] * [cpu]
- memory: 32G
- env:
- CIRRUS_CLONE_DEPTH: 50
- optflags: '-O1'
- debugflags: '-ggdb3'
- RUBY_PREFIX: /tmp/ruby-prefix
- RUBY_DEBUG: ci rgengc
- RUBY_TESTOPTS: >-
- -q
- --color=always
- --tty=no
- matrix:
- CC: clang-12
- CC: gcc-11
- id_script: id
- set_env_script:
- # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using
- # the `make` environment variable used in compilers.yml causes some rubygems
- # tests to fail.
- # https://github.com/rubygems/rubygems/issues/4921
- - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> $CIRRUS_ENV
- print_env_script:
- - echo "GNUMAKEFLAGS=$GNUMAKEFLAGS"
- # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6
- # See https://github.com/aws/containers-roadmap/issues/835
- disable_ipv6_script: sudo ./tool/disable_ipv6.sh
- autogen_script: ./autogen.sh
- configure_script: >-
- ./configure -C
- --enable-debug-env
- --disable-install-doc
- --with-ext=-test-/cxxanyargs,+
- --prefix="$RUBY_PREFIX"
- make_extract-extlibs_script: make extract-extlibs
- make_incs_script: make incs
- make_script: make
- make_leaked-globals_script: make leaked-globals
- make_test_script: make test
- make_install_script: make install
- install_gems_for_test_script: $RUBY_PREFIX/bin/gem install --no-doc timezone tzinfo
- make_test-tool_script: make test-tool
- make_test-all_script: make test-all
- make_test-spec_script: make test-spec
diff --git a/.document b/.document
index ec2fa09326..3a6b0c238c 100644
--- a/.document
+++ b/.document
@@ -18,15 +18,18 @@ gc.rb
io.rb
kernel.rb
marshal.rb
+mjit.rb
numeric.rb
nilclass.rb
pack.rb
ractor.rb
string.rb
+symbol.rb
timev.rb
thread_sync.rb
trace_point.rb
warning.rb
+yjit.rb
# the lib/ directory (which has its own .document file)
lib
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index c8d7ec5e0d..0000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,11 +0,0 @@
-# Lines starting with '#' are comments.
-# Each line is a file pattern followed by one or more owners.
-# Code owners will be automatically tagged as reviewers when a pull request is opened
-
-# YJIT sources and tests
-yjit* @maximecb @xrxr @tenderlove
-yjit/* @maximecb @xrxr @tenderlove
-doc/yjit/* @maximecb @xrxr @tenderlove
-bootstraptest/test_yjit* @maximecb @xrxr @tenderlove
-test/ruby/test_yjit* @maximecb @xrxr @tenderlove
-.github/workflows/yjit* @maximecb @xrxr @tenderlove
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index b18fd29357..bc63aca35b 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -3,4 +3,4 @@ updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
- interval: 'weekly'
+ interval: 'monthly'
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index 1c314da911..ebaafe3bf0 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -4,22 +4,36 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
baseruby:
name: BASERUBY
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
strategy:
matrix:
@@ -34,12 +48,12 @@ jobs:
- ruby-3.1
steps:
- - uses: actions/checkout@v3
- - uses: actions/cache@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
- - uses: ruby/setup-ruby@v1
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
@@ -51,7 +65,7 @@ jobs:
- run: make incs
- run: make all
- run: make test
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -59,7 +73,7 @@ jobs:
"env": "${{ github.workflow }} / BASERUBY @ ${{ matrix.ruby }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml
index f6f8b9a45b..070c0fa1dd 100644
--- a/.github/workflows/bundled_gems.yml
+++ b/.github/workflows/bundled_gems.yml
@@ -2,18 +2,31 @@ name: bundled_gems
on:
push:
+ branches: [ "master" ]
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
pull_request:
+ branches: [ "master" ]
+ paths:
+ - '.github/workflows/bundled_gems.yml'
+ - 'gems/bundled_gems'
+ merge_group:
+ branches: [ "master" ]
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
schedule:
- cron: '45 6 * * *'
+ workflow_dispatch:
+
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
jobs:
update:
+ permissions:
+ contents: write # for Git to git push
if: ${{ github.event_name != 'schedule' || github.repository == 'ruby/ruby' }}
name: update ${{ github.workflow }}
runs-on: ubuntu-latest
@@ -28,9 +41,9 @@ jobs:
echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
echo "TODAY=$(date +%F)" >> $GITHUB_ENV
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache-${{ github.sha }}
@@ -68,14 +81,14 @@ jobs:
[g, v] unless last[g] == v
end
changed, added = changed.partition {|g, _| last[g]}
- news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
+ news.sub!(/^\*( +)The following #{type} gems? are updated\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
end or next
- news.sub!(/^\*( +)The following default gems are now bundled gems\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
+ news.sub!(/^\*( +)The following default gems are now bundled gems\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
added.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
- end or next if added
+ end or next unless added.empty?
File.write("NEWS.md", news)
end
shell: ruby {0}
@@ -137,3 +150,17 @@ jobs:
GIT_AUTHOR_NAME: git
GIT_COMMITTER_NAME: git
if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }}
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / update",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index fab1989335..79b2916feb 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,11 +26,14 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
update-deps:
strategy:
matrix:
- os: [ubuntu-20.04]
+ os: [ubuntu-22.04]
fail-fast: true
runs-on: ${{ matrix.os }}
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
@@ -34,15 +46,14 @@ jobs:
if: ${{ contains(matrix.os, 'ubuntu') }}
- name: Install libraries
run: |
- brew upgrade
brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
if: ${{ contains(matrix.os, 'macos') }}
- name: git config
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
- - uses: actions/cache@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
@@ -52,7 +63,7 @@ jobs:
- run: make all golf
- run: ruby tool/update-deps --fix
- run: git diff --no-ext-diff --ignore-submodules --exit-code
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -60,7 +71,7 @@ jobs:
"env": "${{ matrix.os }} / Dependencies need to update",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml
deleted file mode 100644
index 32a07f7fd6..0000000000
--- a/.github/workflows/check_misc.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-name: Miscellaneous checks
-on: [push, pull_request]
-
-concurrency:
- group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
- cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
-
-jobs:
- checks:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - name: Check if C-sources are US-ASCII
- run: |
- ! grep -r -n '[^ -~]' *.[chy] include internal win32/*.[ch]
- - name: Check for trailing spaces
- run: |
- ! git grep -n '[ ]$' '*.rb' '*.[chy]'
- - name: Check for header macros
- run: |
- ! for header in ruby/*.h; do \
- git grep -l -F -e $header -e HAVE_`echo $header | tr a-z./ A-Z__` -- . > /dev/null || echo $header
- done | grep -F .
- working-directory: include
-
- - uses: actions/cache@v3
- with:
- path: .downloaded-cache
- key: downloaded-cache-${{ github.sha }}
- restore-keys: |
- downloaded-cache
-
- - name: Download previous gems list
- run: |
- data=default_gems.json
- mkdir -p .downloaded-cache
- ln -s .downloaded-cache/$data .
- curl -O -R -z ./$data https://stdgems.org/$data
-
- - name: Make default gems list
- run: |
- #!ruby
- require 'rubygems'
- $:.unshift "lib"
- rgver = File.foreach("lib/rubygems.rb") do |line|
- break $1 if /^\s*VERSION\s*=\s*"([^"]+)"/ =~ line
- end
- gems = Dir.glob("{ext,lib}/**/*.gemspec").map do |f|
- spec = Gem::Specification.load(f)
- "#{spec.name} #{spec.version}"
- end.sort
- File.open("gems/default_gems", "w") do |f|
- f.puts "RubyGems #{rgver}"
- f.puts gems
- end
- shell: ruby --disable=gems {0}
-
- - name: Maintain updated gems list in NEWS
- run: |
- #!ruby
- require 'json'
- news = File.read("NEWS.md")
- prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1]
- prevs = [prev, prev.sub(/\.\d+\z/, '')]
- %W[default].each do |type|
- last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g|
- v = g['versions'].values_at(*prevs).compact.first
- g = g['gem']
- g = 'RubyGems' if g == 'rubygems'
- [g, v] if v
- end.to_h
- changed = File.foreach("gems/#{type}_gems").filter_map do |l|
- next if l.start_with?("#")
- g, v = l.split(" ", 3)
- [g, v] unless last[g] == v
- end
- news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do
- mark = "#{$1} * "
- changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
- end or next
- File.write("NEWS.md", news)
- end
- shell: ruby {0}
-
- - name: Check diffs
- id: diff
- run: |
- git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md
- continue-on-error: true
- - name: Commit
- run: |
- git pull --ff-only origin ${GITHUB_REF#refs/heads/}
- git commit --message="Update default gems list at ${GITHUB_SHA:0:30} [ci skip]" NEWS.md
- git push origin ${GITHUB_REF#refs/heads/}
- env:
- EMAIL: svn-admin@ruby-lang.org
- GIT_AUTHOR_NAME: git
- GIT_COMMITTER_NAME: git
- if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.diff.outcome == 'failure' }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index f9fa0a7449..8dba76fbe2 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,31 +1,42 @@
name: "Code scanning - action"
on:
- push:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
- pull_request:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ # push:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
+ # pull_request:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
schedule:
- - cron: '0 12 * * 4'
+ - cron: '0 12 * * *'
+ workflow_dispatch:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
+ permissions:
+ actions: read # for github/codeql-action/init to get workflow details
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/autobuild to send a status report
runs-on: ubuntu-latest
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ # CodeQL fails to run pull requests from dependabot due to missing write access to upload results.
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') && github.event.head_commit.pusher.name != 'dependabot[bot]' }}
env:
enable_install_doc: no
@@ -38,9 +49,9 @@ jobs:
sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .downloaded-cache
key: downloaded-cache
@@ -49,15 +60,16 @@ jobs:
run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
with:
config-file: ./.github/codeql/codeql-config.yml
+ trap-caching: false
- name: Set ENV
run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
index 1ccc5dbbc8..caf12cc0f4 100644
--- a/.github/workflows/compilers.yml
+++ b/.github/workflows/compilers.yml
@@ -4,13 +4,20 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
- - '**.md'
+ - '**/man'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.rdoc'
- '**/.document'
@@ -22,7 +29,7 @@ concurrency:
# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
# restriction.
env:
- default_cc: clang-14
+ default_cc: clang-15
append_cc: ''
# -O1 is faster than -O3 in our tests... Majority of time are consumed trying
@@ -56,6 +63,9 @@ env:
--color=always
--tty=no
+permissions:
+ contents: read
+
jobs:
compile:
strategy:
@@ -70,44 +80,29 @@ jobs:
- { name: gcc-9, env: { default_cc: gcc-9 } }
- { name: gcc-8, env: { default_cc: gcc-8 } }
- { name: gcc-7, env: { default_cc: gcc-7 } }
- - { name: gcc-6, env: { default_cc: gcc-6 } }
- - { name: gcc-5, env: { default_cc: gcc-5 } }
- - { name: gcc-4.8, env: { default_cc: gcc-4.8 } }
- - name: 'gcc-11 LTO'
- container: gcc-11
+ - name: 'gcc-13 LTO'
+ container: gcc-13
env:
- default_cc: 'gcc-11 -flto=auto -ffat-lto-objects'
+ default_cc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
optflags: '-O2'
shared: disable
# check: true
- - name: 'gcc-11 annocheck'
- container: gcc-11
- env:
- # Minimal flags to pass the check.
- default_cc: 'gcc-11 -O2 -fcf-protection -Wa,--generate-missing-build-notes=yes'
- LDFLAGS: '-Wl,-z,now'
- # FIXME: Drop skipping options
- # https://bugs.ruby-lang.org/issues/18061
- # https://sourceware.org/annobin/annobin.html/Test-pie.html
- TEST_ANNOCHECK_OPTS: "--skip-pie"
- check: true
+ - { name: clang-16, env: { default_cc: clang-16 } }
- { name: clang-15, env: { default_cc: clang-15 } }
- { name: clang-14, env: { default_cc: clang-14 } }
- { name: clang-13, env: { default_cc: clang-13 } }
- { name: clang-12, env: { default_cc: clang-12 } }
- { name: clang-11, env: { default_cc: clang-11 } }
- { name: clang-10, env: { default_cc: clang-10 } }
- - { name: clang-9, env: { default_cc: clang-9 } }
- - { name: clang-8, env: { default_cc: clang-8 } }
- - { name: clang-7, env: { default_cc: clang-7 } }
- - { name: clang-6.0, env: { default_cc: clang-6.0 } }
- - { name: clang-5.0, env: { default_cc: clang-5.0 } }
- - { name: clang-4.0, env: { default_cc: clang-4.0 } }
- - { name: clang-3.9, env: { default_cc: clang-3.9 } }
- - name: 'clang-14 LTO'
- container: clang-14
+ # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o.
+ - { name: clang-9, env: { default_cc: clang-9, append_configure: '--disable-yjit' } }
+ - { name: clang-8, env: { default_cc: clang-8, append_configure: '--disable-yjit' } }
+ - { name: clang-7, env: { default_cc: clang-7, append_configure: '--disable-yjit' } }
+ - { name: clang-6.0, env: { default_cc: clang-6.0, append_configure: '--disable-yjit' } }
+ - name: 'clang-16 LTO'
+ container: clang-16
env:
- default_cc: 'clang-14 -flto=auto'
+ default_cc: 'clang-16 -flto=auto'
optflags: '-O2'
shared: disable
# check: true
@@ -173,7 +168,11 @@ jobs:
# - { name: VM_CHECK_MODE, env: { cppflags: '-DVM_CHECK_MODE' } }
- { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } }
- - { name: USE_FLONUM=0, env: { cppflags: '-DUSE_FLONUM=0' } }
+ - name: USE_FLONUM=0,
+ env:
+ cppflags: '-DUSE_FLONUM=0'
+ # yjit requires FLONUM for the pointer tagging scheme
+ append_configure: '--disable-yjit'
# - { name: USE_GC_MALLOC_OBJ_INFO_DETAILS, env: { cppflags: '-DUSE_GC_MALLOC_OBJ_INFO_DETAILS' } }
- { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } }
# - { name: USE_RINCGC=0, env: { cppflags: '-DUSE_RINCGC=0' } }
@@ -214,7 +213,7 @@ jobs:
name: ${{ matrix.entry.name }}
runs-on: ubuntu-latest
container:
- image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-14' }}
+ image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }}
options: --user root
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
env: ${{ matrix.entry.env || matrix.env }}
@@ -226,10 +225,10 @@ jobs:
- name: setenv
run: |
echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -257,11 +256,13 @@ jobs:
- run: make test-all TESTS='-- ruby -ext-'
if: ${{ matrix.entry.check }}
- run: make test-spec
+ env:
+ CHECK_LEAKS: true
if: ${{ matrix.entry.check }}
- run: make test-annocheck
if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -269,7 +270,7 @@ jobs:
"env": "${{ github.workflow }} / ${{ matrix.entry.name }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 72f28a7b61..d8dc58b119 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,14 +26,18 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
test_task: ["check"] # "test-bundler-parallel", "test-bundled-gems"
os:
- - macos-11
- - macos-12
+ - macos-13
+ - macos-14
+ - macos-15
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
@@ -37,21 +50,21 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: Install libraries
run: |
- brew upgrade
- brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
+ brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline bison
working-directory: src
- name: Set ENV
run: |
echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
+ echo "PATH="/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH"" >> $GITHUB_ENV
- run: ./autogen.sh
working-directory: src
- name: Run configure
@@ -81,7 +94,7 @@ jobs:
PRECHECK_BUNDLED_GEMS: "no"
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -89,7 +102,7 @@ jobs:
"env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index 6f93aa5392..0df917d3d8 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,6 +26,9 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
# Notes:
# Actions console encoding causes issues, see test-all & test-spec steps
#
@@ -28,19 +40,16 @@ jobs:
MSYSTEM: ${{ matrix.msystem }}
MSYS2_ARCH: x86_64
CHOST: "x86_64-w64-mingw32"
- CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe -fstack-protector-strong"
+ CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
CXXFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
CPPFLAGS: "-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048"
- LDFLAGS: "-pipe -fstack-protector-strong"
+ LDFLAGS: "-pipe"
UPDATE_UNICODE: "UNICODE_FILES=. UNICODE_PROPERTY_FILES=. UNICODE_AUXILIARY_FILES=. UNICODE_EMOJI_FILES=."
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
strategy:
matrix:
include:
- - msystem: "MINGW64"
- base_ruby: 2.6
- test_task: "check"
- test-all-opts: "--name=!/TestObjSpace#test_reachable_objects_during_iteration/"
+ # To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses.
- msystem: "UCRT64"
base_ruby: head
test_task: "check"
@@ -56,21 +65,20 @@ jobs:
git config --global core.eol lf
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: Set up Ruby & MSYS2
- uses: ruby/setup-ruby@v1
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.base_ruby }}
- name: set env
run: |
echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV
- echo "TEST_JOBS=$((15 * NUMBER_OF_PROCESSORS / 10))" >> $GITHUB_ENV
- name: where check
run: |
@@ -116,7 +124,7 @@ jobs:
make update-gems
- name: make all
- timeout-minutes: 20
+ timeout-minutes: 30
run: |
make
@@ -140,7 +148,7 @@ jobs:
make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }}
env:
RUBY_TESTOPTS: >-
- -j${{env.TEST_JOBS}} --retry --job-status=normal --show-skip --timeout-scale=1.5
+ --retry --job-status=normal --show-skip --timeout-scale=1.5
${{ matrix.test-all-opts }}
BUNDLER_VERSION:
if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}}
@@ -151,7 +159,7 @@ jobs:
make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }}
if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/')}}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -159,7 +167,7 @@ jobs:
"env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml
new file mode 100644
index 0000000000..26f8a1b2aa
--- /dev/null
+++ b/.github/workflows/mjit-bindgen.yml
@@ -0,0 +1,104 @@
+name: MJIT bindgen
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ include:
+ - task: mjit-bindgen
+ fail-fast: false
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y \
+ build-essential \
+ libssl-dev libyaml-dev libreadline6-dev \
+ zlib1g-dev libncurses5-dev libffi-dev \
+ libclang1-14 \
+ bison autoconf
+ sudo apt-get install -q -y pkg-config || :
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.1'
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug
+ - run: make incs
+ - run: make
+ - run: make install
+ - run: make ${{ matrix.task }}
+ - run: git diff --exit-code
+ working-directory: src
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml
index b5065288c7..6f7181489a 100644
--- a/.github/workflows/mjit.yml
+++ b/.github/workflows/mjit.yml
@@ -3,32 +3,47 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
- test_task: [ "check" ] # to make job names consistent
- jit_opts: [ "--mjit", "--mjit-wait" ]
+ test_task: [check] # to make job names consistent
+ mjit_opts: [--mjit-wait]
fail-fast: false
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
env:
TESTOPTS: '-q --tty=no'
- RUN_OPTS: '--disable-gems ${{ matrix.jit_opts }} --mjit-debug=-ggdb3'
+ RUN_OPTS: '--disable-gems ${{ matrix.mjit_opts }} --mjit-debug=-ggdb3'
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
steps:
- run: mkdir build
@@ -42,10 +57,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -64,31 +79,30 @@ jobs:
- run: make incs
- run: make
- run: sudo make -s install
- - run: sudo apt-get install gdb # used by test / test-all failure
- name: Run test
run: |
- ulimit -c unlimited
+ unset GNUMAKEFLAGS
make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
- - name: Run test-all
- run: |
- ulimit -c unlimited
- make -s test-all RUN_OPTS="$RUN_OPTS"
- timeout-minutes: 60
+ # - name: Run test-all
+ # run: |
+ # ulimit -c unlimited
+ # make -s test-all RUN_OPTS="$RUN_OPTS"
+ # timeout-minutes: 60
- name: Run test-spec
run: |
- ulimit -c unlimited
+ unset GNUMAKEFLAGS
make -s test-spec RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.jit_opts }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.mjit_opts }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000000..5d4474d978
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,18 @@
+name: Start release workflow
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build release package
+ run: |
+ curl -L -X POST \
+ -H "Authorization: Bearer ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/ruby/actions/dispatches \
+ -d '{"event_type": "${{ github.ref }}"}'
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
new file mode 100644
index 0000000000..c12a95362d
--- /dev/null
+++ b/.github/workflows/scorecards.yml
@@ -0,0 +1,72 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecards supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '22 4 * * 2'
+ push:
+ branches: [ "master" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecards analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@ea651e62978af7915d09fe2e282747c798bf2dab # v2.4.1
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecards on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
+ repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.27
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
index d09f1a24d3..4521195a2b 100644
--- a/.github/workflows/spec_guards.yml
+++ b/.github/workflows/spec_guards.yml
@@ -2,45 +2,61 @@ name: Rubyspec Version Guards Check
on:
push:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
pull_request:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
+ merge_group:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
rubyspec:
name: Rubyspec
- runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ runs-on: ubuntu-22.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
strategy:
matrix:
# Specs from ruby/spec should still run on all supported Ruby versions.
# This also ensures the needed ruby_version_is guards are there, see spec/README.md.
ruby:
- - ruby-2.7
- ruby-3.1
+ - ruby-3.2
steps:
- - uses: actions/checkout@v3
- - uses: ruby/setup-ruby@v1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
+
- run: gem install webrick
+
- run: ruby ../mspec/bin/mspec
working-directory: spec/ruby
- - uses: k0kubun/action-slack@v2.0.0
+ env:
+ CHECK_LEAKS: true
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -48,8 +64,8 @@ jobs:
"env": "${{ github.workflow }} / rubyspec @ ${{ matrix.ruby }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 4e4d1a02e1..4fbca1170e 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,37 +26,39 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
- test_task: ["check", "test-bundler-parallel", "test-bundled-gems"]
- os:
- - ubuntu-20.04
-# - ubuntu-18.04
- configure: ["", "cppflags=-DRUBY_DEBUG"]
+ # main variables included in the job name
+ test_task: [check]
+ configure: [cppflags=-DRUBY_DEBUG] # default to use more assertions
+ arch: ['']
+ # specify all jobs with `include` to avoid testing duplicated things
include:
- - test_task: "check"
- configure: ""
+ - test_task: check
+ - test_task: check
arch: i686
- - test_task: "check"
+ configure: '' # test without -DRUBY_DEBUG as well
+ - test_task: check
configure: "--enable-shared --enable-load-relative"
- skipped_tests: "TestGem#test_.*_from_binstubs.*"
- continue-on-skipped_tests: true
- - test_task: "test-all TESTS=--repeat-count=2"
+ - test_task: test-all TESTS=--repeat-count=2
+ - test_task: test-bundler-parallel
+ - test_task: test-bundled-gems
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
RUBY_DEBUG: ci
SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
- runs-on: ${{ matrix.os || 'ubuntu-20.04' }}
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
working-directory:
- name: Set ENV
- env:
- configure: ${{matrix.configure}}
run: |
echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- name: Install libraries
@@ -68,10 +79,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -116,15 +127,15 @@ jobs:
TESTS: ${{ matrix.skipped_tests }}
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index 713444b741..27920b5821 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,6 +26,9 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
jobs:
make:
strategy:
@@ -38,7 +50,7 @@ jobs:
WASI_SDK_VERSION_MINOR: 0
BINARYEN_VERSION: 109
WASMTIME_VERSION: v0.33.0
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
@@ -47,7 +59,7 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- name: Install libraries
@@ -79,6 +91,18 @@ jobs:
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
- run: ./autogen.sh
working-directory: src
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.0'
+ bundler: none
+
+ - name: Download config.guess with wasi version
+ run: |
+ rm tool/config.guess tool/config.sub
+ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub
+ working-directory: src
+
- name: Run configure
run: |
../src/configure \
@@ -92,7 +116,8 @@ jobs:
debugflags="${{ matrix.entry.debugflags }}" \
wasmoptflags="${{ matrix.entry.wasmoptflags }} ${{ matrix.entry.debugflags }}"
- - run: make ruby
+ # miniruby may not be built when cross-compling
+ - run: make mini ruby
- name: Run basictest
run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb
working-directory: src
@@ -102,6 +127,20 @@ jobs:
ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS"
working-directory: src
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.name }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
defaults:
run:
working-directory: build
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 4418c78d81..c2bd4881c2 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,90 +26,76 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
make:
strategy:
matrix:
include:
- - vs: 2019
- vs: 2022
+ vcvers: -vcvars_ver=14.2
fail-fast: false
- runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ runs-on: windows-${{ matrix.vs }}
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
name: VisualStudio ${{ matrix.vs }}
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
PATCH: C:\msys64\usr\bin\patch.exe
- OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ OS_VER: windows-${{ matrix.vs }}
steps:
- run: md build
working-directory:
- - uses: msys2/setup-msys2@v2
+ - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
id: setup-msys2
with:
update: true
- install: >-
- patch
- if: ${{ env.OS_VER != 'windows-2019' }}
+ install: bison patch
- name: patch path
shell: msys2 {0}
run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV
if: ${{ steps.setup-msys2.outcome == 'success' }}
- - uses: actions/cache@v3
- with:
- path: C:\vcpkg\downloads
- key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-
- ${{ runner.os }}-vcpkg-download-
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: C:\vcpkg\installed
- key: ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-${{ github.sha }}
+ key: ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-
- ${{ runner.os }}-vcpkg-installed-
+ ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-
+ ${{ runner.os }}-vcpkg-installed-windows-
- name: Install libraries with vcpkg
run: |
+ iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
+ Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
+ scoop install cmake@3.31.6
vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
- - uses: actions/cache@v3
- with:
- path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
- key: ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-
- ${{ runner.os }}-chocolatey-
- - name: Install libraries with chocolatey
- run: |
- # Using Choco-Install for retries, but it doesn't detect failures properly
- # if you pass multiple package names in a single command.
- Choco-Install -PackageName winflexbison3
- shell: pwsh
+ shell:
+ pwsh
- name: git config
run: |
git config --global core.autocrlf false
git config --global core.eol lf
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
- name: setup env
# %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
# https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
+ # msys2/setup-msys2 installs MSYS2 to D:/a/_temp/msys64/usr/bin
run: |
- set VS=${{ matrix.vs }}
- set VCVARS=${{ matrix.vcvars }}
+ set Path=D:/a/_temp/msys64/usr/bin;%Path%
if not "%VCVARS%" == "" goto :vcset
- set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
:vcset
set | C:\msys64\usr\bin\sort > old.env
- call %VCVARS%
+ call %VCVARS% ${{ matrix.vcvers || ''}}
set TMP=%USERPROFILE%\AppData\Local\Temp
set TEMP=%USERPROFILE%\AppData\Local\Temp
set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul
@@ -125,18 +120,16 @@ jobs:
- run: nmake extract-extlibs
- run: nmake
env:
- YACC: win_bison
+ YACC: bison.exe
- run: nmake test
timeout-minutes: 5
+ - run: nmake test-spec
+ timeout-minutes: 10
- run: nmake test-all
env:
RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal
timeout-minutes: 60
- continue-on-error: ${{ matrix.continue-on-error || false }}
- - run: nmake test-spec
- timeout-minutes: 10
- continue-on-error: ${{ matrix.continue-on-error || false }}
- - uses: k0kubun/action-slack@v2.0.0
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
@@ -144,7 +137,7 @@ jobs:
"env": "VS${{ matrix.vs }} / ${{ matrix.test_task || 'check' }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
index bf90b80efb..0b7b9046e9 100644
--- a/.github/workflows/yjit-ubuntu.yml
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -3,12 +3,21 @@ on:
push:
paths-ignore:
- 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
pull_request:
paths-ignore:
- 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
@@ -17,13 +26,16 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+permissions:
+ contents: read
+
jobs:
cargo:
name: Rust cargo test
# GitHub Action's image seems to already contain a Rust 1.58.0.
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# For now we can't run cargo test --offline because it complains about the
# capstone dependency, even though the dependency is optional
#- run: cargo test --offline
@@ -40,30 +52,40 @@ jobs:
fail-fast: false
matrix:
include:
- - test_task: "check-yjit-bindings"
- configure: "--with-gcc=clang-12 --enable-yjit=dev"
+ - test_task: 'yjit-bindgen'
+ hint: 'To fix: use patch in logs'
+ configure: '--with-gcc=clang-14 --enable-yjit=dev'
+ libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
- test_task: "check"
- configure: "--enable-yjit RUSTC='rustc +1.58.1'" # release build
- rust_version: "1.58.1"
+ # YJIT should be automatically built in release mode on x86-64 Linux with rustc present
+ #configure: "--enable-yjit RUSTC='rustc +1.58.0'"
+ configure: "RUSTC='rustc +1.58.0'"
+ rust_version: "1.58.0"
- test_task: "check"
configure: "--enable-yjit=dev"
- test_task: "check"
configure: "--enable-yjit=dev"
- yjit_opts: "--yjit-call-threshold=1"
+ yjit_opts: "--yjit-call-threshold=1 --yjit-verify-ctx"
- test_task: "test-all TESTS=--repeat-count=2"
configure: "--enable-yjit=dev"
- test_task: "test-bundled-gems"
configure: "--enable-yjit=dev"
+
+ - test_task: "yjit-bench"
+ configure: "--enable-yjit=dev"
+ yjit_bench_opts: "--yjit-stats"
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
RUN_OPTS: ${{ matrix.yjit_opts }}
+ YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
RUBY_DEBUG: ci
- runs-on: ubuntu-20.04
+ BUNDLE_JOBS: 8 # for yjit-bench
+ runs-on: ubuntu-22.04
if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
- run: mkdir build
@@ -80,10 +102,10 @@ jobs:
run: |
git config --global advice.detachedHead 0
git config --global init.defaultBranch garbage
- - uses: actions/checkout@v3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: src
- - uses: actions/cache@v3
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src/.downloaded-cache
key: downloaded-cache
@@ -98,7 +120,7 @@ jobs:
- run: ./autogen.sh
working-directory: src
- name: Run configure
- run: ../src/configure -C --disable-install-doc ${{ matrix.configure }}
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }}
- run: make incs
- run: make prepare-gems
if: ${{ matrix.test_task == 'test-bundled-gems' }}
@@ -111,21 +133,33 @@ jobs:
if: ${{ matrix.test_task == 'check' }}
- name: Enable YJIT through ENV
run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
- - run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS"
+ # Check that the binary was built with YJIT
+ - name: Check YJIT enabled
+ run: ./miniruby --yjit -v | grep "+YJIT"
+ - name: make ${{ matrix.test_task }}
+ run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS"
timeout-minutes: 60
env:
RUBY_TESTOPTS: "-q --tty=no"
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
PRECHECK_BUNDLED_GEMS: "no"
- - uses: k0kubun/action-slack@v2.0.0
+ LIBCLANG_PATH: ${{ matrix.libclang_path }}
+ continue-on-error: ${{ matrix.test_task == 'yjit-bench' }}
+ - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
+ run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}"
+ env:
+ BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
+ if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }}
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
with:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
- "branch": "${{ github.ref }}".split('/').reverse()[0]
+ "branch": "${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.gitignore b/.gitignore
index c12ec27782..99d32a1825 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@
*.inc
*.log
*.o
+*.o.tmp
*.obj
*.old
*.orig
@@ -25,6 +26,7 @@
*.sav
*.sl
*.so
+*.so.*
*.swp
*.yarb
*~
@@ -145,6 +147,8 @@ lcov*.info
/bin/*.exe
/bin/*.dll
+/bin/goruby
+/bin/ruby
# /benchmark/
/benchmark/bm_require.data
@@ -233,9 +237,14 @@ lcov*.info
/win32/*.ico
# MJIT
-/rb_mjit_header.h
-/mjit_config.h
/include/ruby-*/*/rb_mjit_min_header-*.h
+/lib/ruby_vm/mjit/instruction.rb
+/mjit_config.h
+/rb_mjit_header.h
+
+# YJIT
+/yjit-bench
+/yjit_exit_locations.dump
# /wasm/
/wasm/tests/*.wasm
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6875c766a9..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,234 +0,0 @@
-# -*- YAML -*-
-# Copyright (C) 2011 Urabe, Shyouhei. All rights reserved.
-#
-# This file is a part of the programming language Ruby. Permission is hereby
-# granted, to either redistribute or modify this file, provided that the
-# conditions mentioned in the file COPYING are met. Consult the file for
-# details.
-
-# We only manage non-amd64 free pipelines.
-# https://docs.travis-ci.com/user/billing-overview/
-
-language: c
-
-os: linux
-
-if: commit_message !~ /\[DOC\]/
-
-dist: focal
-
-git:
- quiet: true
-
-cache:
- ccache: true
- directories:
- - $HOME/config_2nd
- - $HOME/.downloaded-cache
-
-env:
- global:
- # The tests skipped in `make test-all`.
- - TEST_ALL_SKIPPED_TESTS=
- # The tests executed separately by `make test-all`.
- - TEST_ALL_SEPARATED_TESTS=
- # Reset timestamps early
- - _=$(touch NEWS && find . -type f -exec touch -r NEWS {} +)
- - CONFIGURE_TTY=no
- - CCACHE_COMPILERCHECK=none
- - CCACHE_NOCOMPRESS=1
- - CCACHE_MAXSIZE=512Mi
- - NPROC="`nproc`"
- # JOBS and SETARCH are overridden when necessary; see below.
- - JOBS=-j$((1+${NPROC}))
- - SETARCH=
- - RUBY_PREFIX=/tmp/ruby-prefix
- - GEMS_FOR_TEST='timezone tzinfo'
- # https://github.com/travis-ci/travis-build/blob/e411371dda21430a60f61b8f3f57943d2fe4d344/lib/travis/build/bash/travis_apt_get_options.bash#L7
- - travis_apt_get_options='--allow-downgrades --allow-remove-essential --allow-change-held-packages'
- - travis_apt_get_options="-yq --no-install-suggests --no-install-recommends $travis_apt_get_options"
- # -O1 is faster than -O3 in our tests.
- - optflags=-O1
- # -g0 disables backtraces when SEGV. Do not set that.
- - debugflags=-ggdb3
-
-.org.ruby-lang.ci.matrix-definitions:
-
- - &gcc-10
- compiler: gcc-10
- before_install:
- - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- - >-
- tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
- gcc-10
- g++-10
- libffi-dev
- libncurses-dev
- libncursesw5-dev
- libreadline-dev
- libssl-dev
- libyaml-dev
- openssl
- zlib1g-dev
-
- # --------
-
- - &arm64-linux
- name: arm64-linux
- arch: arm64
- <<: *gcc-10
-
- - &ppc64le-linux
- name: ppc64le-linux
- arch: ppc64le
- <<: *gcc-10
-
- - &s390x-linux
- name: s390x-linux
- arch: s390x
- <<: *gcc-10
-
- - &arm32-linux
- name: arm32-linux
- arch: arm64
- # https://packages.ubuntu.com/focal/crossbuild-essential-armhf
- compiler: arm-linux-gnueabihf-gcc
- env:
- - SETARCH='setarch linux32 --verbose --32bit'
- # The "TestReadline#test_interrupt_in_other_thread" started failing on arm32
- # from https://www.travis-ci.com/github/ruby/ruby/jobs/529005145
- - TEST_ALL_SKIPPED_TESTS=test_interrupt_in_other_thread
- before_install:
- - sudo dpkg --add-architecture armhf
- - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- - >-
- tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
- crossbuild-essential-armhf
- libc6:armhf
- libstdc++-10-dev:armhf
- libffi-dev:armhf
- libncurses-dev:armhf
- libncursesw5-dev:armhf
- libreadline-dev:armhf
- libssl-dev:armhf
- linux-libc-dev:armhf
- zlib1g-dev:armhf
-
-matrix:
- include:
- # Build every commit (Allowed Failures):
- - <<: *arm32-linux
- # Comment out as the 2nd arm64 pipeline is unstable.
- # - <<: *arm64-linux
- - <<: *ppc64le-linux
- - <<: *s390x-linux
- allow_failures:
- # We see multiple errors indicating errors on the Travis environment itself in a short while:
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544382885
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544361370
- # It's not a fault of Ruby's arm32 support but just Travis arm32 seems unsable.
- - name: arm32-linux
- # - name: arm64-linux
- # We see "Some worker was crashed." in about 40% of recent ppc64le-linux jobs
- # e.g. https://app.travis-ci.com/github/ruby/ruby/jobs/530959548
- - name: ppc64le-linux
- # Tentatively disable, because often hungs up **after** all tests
- # have finished successfully and saving caches.
- - name: s390x-linux
- fast_finish: true
-
-before_script:
- - . tool/ci_functions.sh
- - |-
- if [ -n "${TEST_ALL_SKIPPED_TESTS}" ]; then
- TEST_ALL_OPTS="${TEST_ALL_OPTS} $(ci_to_excluded_test_opts "${TEST_ALL_SKIPPED_TESTS}")"
- if [ -z "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_SEPARATED_TESTS="${TEST_ALL_SKIPPED_TESTS}"
- fi
- fi
- - |-
- if [ -n "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_OPTS_SEPARATED="$(ci_to_included_test_opts "${TEST_ALL_SEPARATED_TESTS}")"
- fi
- - echo TEST_ALL_OPTS="${TEST_ALL_OPTS}" TEST_ALL_OPTS_SEPARATED="${TEST_ALL_OPTS_SEPARATED}"
- - rm -fr .ext autom4te.cache
- - |-
- [ -d ~/.downloaded-cache ] ||
- mkdir ~/.downloaded-cache
- - ln -s ~/.downloaded-cache
- - "> config.status"
- - "> .rbconfig.time"
- - sed -f tool/prereq.status template/Makefile.in common.mk > Makefile
- - make -s $JOBS up
- - make -s $JOBS srcs
- - rm -f config.status Makefile rbconfig.rb .rbconfig.time
- - |-
- if [ -d ~/config_2nd ]; then
- cp -pr ~/config_2nd build
- else
- mkdir build
- fi
- - mkdir config_1st config_2nd
- - chmod -R a-w .
- - chmod -R u+w build config_1st config_2nd
- - cd build
- - |-
- case "$CC" in
- gcc*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-diagnostics-color";;
- clang*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-color-diagnostics";;
- esac
- - |-
- [ ! -f config.cache ] ||
- [ "$CC" = "`sed -n s/^ac_cv_prog_CC=//p config.cache`" ] ||
- (set -x; exec rm config.cache)
- - $SETARCH ../configure -C --disable-install-doc --prefix=$RUBY_PREFIX $CONFIG_FLAG
- - cp -pr config.cache config.status .ext/include ../config_1st
- - $SETARCH make reconfig
- - cp -pr config.cache config.status .ext/include ../config_2nd
- - (cd .. && exec diff -ru config_1st config_2nd)
- - chmod u+w ..
- - rm -rf ~/config_2nd
- - mv ../config_2nd ~
- - chmod u-w ..
- - $SETARCH make -s $JOBS
- - make -s install
- - |-
- [ -z "${GEMS_FOR_TEST}" ] ||
- $RUBY_PREFIX/bin/gem install --no-document $GEMS_FOR_TEST
- - echo "raise 'do not load ~/.irbrc in test'" > ~/.irbrc
-
-script:
- - $SETARCH make -s test -o showflags TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}"
- - ../tool/travis_wait.sh $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -q --tty=no ${TEST_ALL_OPTS}" RUBYOPT="-w"
- # Run the failing tests separately returning ok status to check if it works,
- # visualize them.
- - |
- if [ -n "${TEST_ALL_OPTS_SEPARATED}" ]; then
- $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -v --tty=no ${TEST_ALL_OPTS_SEPARATED}" RUBYOPT="-w" || :
- fi
- - $SETARCH make -s test-spec MSPECOPT=-ff # not using `-j` because sometimes `mspec -j` silently dies
- - $SETARCH make -s -o showflags leaked-globals
-
-# We enable Travis on the specific branches or forked repositories here.
-if: (repo = ruby/ruby AND (branch = master OR branch =~ /^ruby_\d_\d$/)) OR repo != ruby/ruby
-
-# We want to be notified when something happens.
-notifications:
- irc:
- channels:
- - "chat.freenode.net#ruby-core"
- on_success: change # [always|never|change] # default: always
- on_failure: always # [always|never|change] # default: always
- template:
- - "%{message} by @%{author}: See %{build_url}"
-
- webhooks:
- urls:
- - secure: mRsoS/UbqDkKkW5p3AEqM27d4SZnV6Gsylo3bm8T/deltQzTsGzZwrm7OIBXZv0UFZdE68XmPlyHfZFLSP2V9QZ7apXMf9/vw0GtcSe1gchtnjpAPF6lYBn7nMCbVPPx9cS0dwL927fjdRM1vj7IKZ2bk4F0lAJ25R25S6teqdk= # ruby-lang slack: ruby/simpler-alerts-bot (travis)
- on_success: never
- on_failure: always
-
- email:
- - jaruga@ruby-lang.org
diff --git a/LEGAL b/LEGAL
index 9645728efe..0423d57ac9 100644
--- a/LEGAL
+++ b/LEGAL
@@ -979,7 +979,6 @@ mentioned below.
{MIT License}[rdoc-label:label-MIT+License]
[lib/rubygems/resolver/molinillo]
-[lib/bundler/vendor/molinillo]
molinillo is under the following license.
@@ -988,6 +987,15 @@ mentioned below.
{MIT License}[rdoc-label:label-MIT+License]
+[lib/bundler/vendor/pub_grub]
+
+ pub_grub is under the following license.
+
+ >>>
+ Copyright (c) 2018 John Hawthorn
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
[lib/bundler/vendor/connection_pool]
connection_pool is under the following license.
diff --git a/NEWS.md b/NEWS.md
index cc7c9d2a93..f6c3c6fc97 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -23,13 +23,13 @@ Note that each entry is kept to a minimum, see links for details.
* A proc that accepts a single positional argument and keywords will
no longer autosplat. [[Bug #18633]]
- ```ruby
- proc{|a, **k| a}.call([1, 2])
- # Ruby 3.1 and before
- # => 1
- # Ruby 3.2 and after
- # => [1, 2]
- ```
+ ```ruby
+ proc{|a, **k| a}.call([1, 2])
+ # Ruby 3.1 and before
+ # => 1
+ # Ruby 3.2 and after
+ # => [1, 2]
+ ```
* Constant assignment evaluation order for constants set on explicit
objects has been made consistent with single attribute assignment
@@ -39,24 +39,24 @@ Note that each entry is kept to a minimum, see links for details.
foo::BAR = baz
```
- `foo` is now called before `baz`. Similarly, for multiple assignments
- to constants, left-to-right evaluation order is used. With this
- code:
+ `foo` is now called before `baz`. Similarly, for multiple assignments
+ to constants, left-to-right evaluation order is used. With this
+ code:
```ruby
- foo1::BAR1, foo2::BAR2 = baz1, baz2
+ foo1::BAR1, foo2::BAR2 = baz1, baz2
```
- The following evaluation order is now used:
+ The following evaluation order is now used:
- 1. `foo1`
- 2. `foo2`
- 3. `baz1`
- 4. `baz2`
+ 1. `foo1`
+ 2. `foo2`
+ 3. `baz1`
+ 4. `baz2`
- [[Bug #15928]]
+ [[Bug #15928]]
-* Find pattern is no longer experimental.
+* "Find pattern" is no longer experimental.
[[Feature #18585]]
* Methods taking a rest parameter (like `*args`) and wishing to delegate keyword
@@ -90,111 +90,487 @@ Note that each entry is kept to a minimum, see links for details.
foo(k: 1)
```
-## Command line options
-
## Core classes updates
Note: We're only listing outstanding class updates.
+* Fiber
+
+ * Introduce Fiber.[] and Fiber.[]= for inheritable fiber storage.
+ Introduce Fiber#storage and Fiber#storage= (experimental) for
+ getting and resetting the current storage. Introduce
+ `Fiber.new(storage:)` for setting the storage when creating a
+ fiber. [[Feature #19078]]
+
+ Existing Thread and Fiber local variables can be tricky to use.
+ Thread-local variables are shared between all fibers, making it
+ hard to isolate, while Fiber-local variables can be hard to
+ share. It is often desirable to define unit of execution
+ ("execution context") such that some state is shared between all
+ fibers and threads created in that context. This is what Fiber
+ storage provides.
+
+ ```ruby
+ def log(message)
+ puts "#{Fiber[:request_id]}: #{message}"
+ end
+
+ def handle_requests
+ while request = read_request
+ Fiber.schedule do
+ Fiber[:request_id] = SecureRandom.uuid
+
+ request.messages.each do |message|
+ Fiber.schedule do
+ log("Handling #{message}") # Log includes inherited request_id.
+ end
+ end
+ end
+ end
+ end
+ ```
+
+ You should generally consider Fiber storage for any state which
+ you want to be shared implicitly between all fibers and threads
+ created in a given context, e.g. a connection pool, a request
+ id, a logger level, environment variables, configuration, etc.
+
+* Fiber::Scheduler
+
+ * Introduce `Fiber::Scheduler#io_select` for non-blocking IO.select.
+ [[Feature #19060]]
+
+* IO
+
+ * Introduce IO#timeout= and IO#timeout which can cause
+ IO::TimeoutError to be raised if a blocking operation exceeds the
+ specified timeout. [[Feature #18630]]
+
+ ```ruby
+ STDIN.timeout = 1
+ STDIN.read # => Blocking operation timed out! (IO::TimeoutError)
+ ```
+
+ * Introduce `IO.new(..., path:)` and promote `File#path` to `IO#path`.
+ [[Feature #19036]]
+
+* Class
+
+ * Class#attached_object, which returns the object for which
+ the receiver is the singleton class. Raises TypeError if the
+ receiver is not a singleton class.
+ [[Feature #12084]]
+
+ ```ruby
+ class Foo; end
+
+ Foo.singleton_class.attached_object #=> Foo
+ Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
+ Foo.attached_object #=> TypeError: `Foo' is not a singleton class
+ nil.singleton_class.attached_object #=> TypeError: `NilClass' is not a singleton class
+ ```
+
+* Data
+
+ * New core class to represent simple immutable value object. The class is
+ similar to Struct and partially shares an implementation, but has more
+ lean and strict API. [[Feature #16122]]
+
+ ```ruby
+ Measure = Data.define(:amount, :unit)
+ distance = Measure.new(100, 'km') #=> #<data Measure amount=100, unit="km">
+ weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg">
+ weight.with(amount: 40) #=> #<data Measure amount=40, unit="kg">
+ weight.amount #=> 50
+ weight.amount = 40 #=> NoMethodError: undefined method `amount='
+ ```
+
+* Encoding
+
+ * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]]
+ * The dummy `Encoding::UTF_16` and `Encoding::UTF_32` encodings no longer
+ try to dynamically guess the endian based on a byte order mark.
+ Use `Encoding::UTF_16BE`/`UTF_16LE` and `Encoding::UTF_32BE`/`UTF_32LE` instead.
+ This change speeds up getting the encoding of a String. [[Feature #18949]]
+ * Limit maximum encoding set size by 256.
+ If exceeding maximum size, `EncodingError` will be raised. [[Feature #18949]]
+
* Enumerator
+
* Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]]
+* Exception
+
+ * Exception#detailed_message has been added.
+ The default error printer calls this method on the Exception object
+ instead of #message. [[Feature #18564]]
+
* Hash
+
* Hash#shift now always returns nil if the hash is
empty, instead of returning the default value or
calling the default proc. [[Bug #16908]]
* Integer
+
* Integer#ceildiv has been added. [[Feature #18809]]
* Kernel
+
* Kernel#binding raises RuntimeError if called from a non-Ruby frame
(such as a method defined in C). [[Bug #18487]]
* MatchData
+
* MatchData#byteoffset has been added. [[Feature #13110]]
+ * MatchData#deconstruct has been added. [[Feature #18821]]
+ * MatchData#deconstruct_keys has been added. [[Feature #18821]]
* Module
+
* Module.used_refinements has been added. [[Feature #14332]]
* Module#refinements has been added. [[Feature #12737]]
* Module#const_added has been added. [[Feature #17881]]
* Module#undefined_instance_methods has been added. [[Feature #12655]]
* Proc
+
* Proc#dup returns an instance of subclass. [[Bug #17545]]
* Proc#parameters now accepts lambda keyword. [[Feature #15357]]
+* Process
+ * Added `RLIMIT_NPTS` constant to FreeBSD platform
+
* Regexp
+
+ * The cache-based optimization is introduced.
+ Many (but not all) Regexp matching is now in linear time, which
+ will prevent regular expression denial of service (ReDoS)
+ vulnerability. [[Feature #19104]]
+
+ * Regexp.linear_time? is introduced. [[Feature #19194]]
+
* Regexp.new now supports passing the regexp flags not only as an Integer,
- but also as a String Unknown flags raise errors. Otherwise, anything
- other than `true`, `false`, `nil` or Integer will be warned.
+ but also as a String. Unknown flags raise ArgumentError.
+ Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned.
[[Feature #18788]]
+ * Regexp.timeout= has been added. Also, Regexp.new new supports timeout keyword.
+ See [[Feature #17837]]
+
* Refinement
+
* Refinement#refined_class has been added. [[Feature #12737]]
+* RubyVM::AbstractSyntaxTree
+
+ * Add `error_tolerant` option for `parse`, `parse_file` and `of`. [[Feature #19013]]
+ With this option
+
+ 1. SyntaxError is suppressed
+ 2. AST is returned for invalid input
+ 3. `end` is complemented when a parser reaches to the end of input but `end` is insufficient
+ 4. `end` is treated as keyword based on indent
+
+ ```ruby
+ # Without error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ # => <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)
+
+ # With error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ p root # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-4:3>
+
+ # `end` is treated as keyword based on indent
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ module Z
+ class Foo
+ foo.
+ end
+
+ def bar
+ end
+ end
+ RUBY
+ p root.children[-1].children[-1].children[-1].children[-2..-1]
+ # => [#<RubyVM::AbstractSyntaxTree::Node:CLASS@2:2-4:5>, #<RubyVM::AbstractSyntaxTree::Node:DEFN@6:2-7:5>]
+ ```
+
+ * Add `keep_tokens` option for `parse`, `parse_file` and `of`. Add `#tokens` and `#all_tokens`
+ for RubyVM::AbstractSyntaxTree::Node [[Feature #19070]]
+
+ ```ruby
+ root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ ```
+
* Set
+
* Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]]
- It is currently autoloaded via the `Set` constant or a call to `Enumerable#to_set`.
+ It is currently autoloaded via the Set constant or a call to Enumerable#to_set.
* String
+
* String#byteindex and String#byterindex have been added. [[Feature #13110]]
- * Update Unicode to Version 14.0.0 and Emoji Version 14.0. [[Feature #18037]]
+ * Update Unicode to Version 15.0.0 and Emoji Version 15.0. [[Feature #18639]]
(also applies to Regexp)
* String#bytesplice has been added. [[Feature #18598]]
+ * String#dedup has been added as an alias to String#-@. [[Feature #18595]]
* Struct
+
* A Struct class can also be initialized with keyword arguments
- without `keyword_init: true` on `Struct.new` [[Feature #16806]]
+ without `keyword_init: true` on Struct.new [[Feature #16806]]
+
+ ```ruby
+ Post = Struct.new(:id, :name)
+ Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
+ # From Ruby 3.2, the following code also works without keyword_init: true.
+ Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
+ ```
+
+* Thread
+
+ * Thread.each_caller_location is added. [[Feature #16663]]
+
+* Thread::Queue
+
+ * Thread::Queue#pop(timeout: sec) is added. [[Feature #18774]]
+
+* Thread::SizedQueue
+
+ * Thread::SizedQueue#pop(timeout: sec) is added. [[Feature #18774]]
+ * Thread::SizedQueue#push(timeout: sec) is added. [[Feature #18944]]
+
+* Time
+
+ * Time#deconstruct_keys is added, allowing to use Time instances
+ in pattern-matching expressions [[Feature #19071]]
+
+ * Time.new now can parse a string like generated by Time#inspect
+ and return a Time instance based on the given argument.
+ [[Feature #18033]]
+
+* SyntaxError
+ * SyntaxError#path has been added. [[Feature #19138]]
* TracePoint
+
* TracePoint#binding now returns `nil` for `c_call`/`c_return` TracePoints.
[[Bug #18487]]
* TracePoint#enable `target_thread` keyword argument now defaults to the
- current thread if `target` and `target_line` keyword arguments are not
- passed. [[Bug #16889]]
+ current thread if a block is given and `target` and `target_line` keyword
+ arguments are not passed. [[Bug #16889]]
+
+* UnboundMethod
+
+ * `UnboundMethod#==` returns `true` if the actual method is same. For example,
+ `String.instance_method(:object_id) == Array.instance_method(:object_id)`
+ returns `true`. [[Feature #18798]]
+
+ * `UnboundMethod#inspect` does not show the receiver of `instance_method`.
+ For example `String.instance_method(:object_id).inspect` returns
+ `"#<UnboundMethod: Kernel#object_id()>"`
+ (was `"#<UnboundMethod: String(Kernel)#object_id()>"`).
+
+* GC
+
+ * Expose `need_major_gc` via `GC.latest_gc_info`. [GH-6791]
+
+* ObjectSpace
+
+ * `ObjectSpace.dump_all` dump shapes as well. [GH-6868]
## Stdlib updates
+* Bundler
+
+ * Bundler now uses [PubGrub] resolver instead of [Molinillo] for performance improvement.
+ * Add --ext=rust support to bundle gem for creating simple gems with Rust extensions.
+ [[GH-rubygems-6149]]
+ * Make cloning git repos faster [[GH-rubygems-4475]]
+
+* RubyGems
+
+ * Add mswin support for cargo builder. [[GH-rubygems-6167]]
+
+* CGI
+
+ * `CGI.escapeURIComponent` and `CGI.unescapeURIComponent` are added.
+ [[Feature #18822]]
+
+* Coverage
+
+ * `Coverage.setup` now accepts `eval: true`. By this, `eval` and related methods are
+ able to generate code coverage. [[Feature #19008]]
+
+ * `Coverage.supported?(mode)` enables detection of what coverage modes are
+ supported. [[Feature #19026]]
+
+* Date
+
+ * Added `Date#deconstruct_keys` and `DateTime#deconstruct_keys` same as [[Feature #19071]]
+
+* ERB
+
+ * `ERB::Util.html_escape` is made faster than `CGI.escapeHTML`.
+ * It no longer allocates a String object when no character needs to be escaped.
+ * It skips calling `#to_s` method when an argument is already a String.
+ * `ERB::Escape.html_escape` is added as an alias to `ERB::Util.html_escape`,
+ which has not been monkey-patched by Rails.
+ * `ERB::Util.url_encode` is made faster using `CGI.escapeURIComponent`.
+ * `-S` option is removed from `erb` command.
+
+* FileUtils
+
+ * Add FileUtils.ln_sr method and `relative:` option to FileUtils.ln_s.
+ [[Feature #18925]]
+
+* IRB
+
+ * debug.gem integration commands have been added: `debug`, `break`, `catch`,
+ `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info`
+ * They work even if you don't have `gem "debug"` in your Gemfile.
+ * See also: [What's new in Ruby 3.2's IRB?](https://st0012.dev/whats-new-in-ruby-3-2-irb)
+ * More Pry-like commands and features have been added.
+ * `edit` and `show_cmds` (like Pry's `help`) are added.
+ * `ls` takes `-g` or `-G` option to filter out outputs.
+ * `show_source` is aliased from `$` and accepts unquoted inputs.
+ * `whereami` is aliased from `@`.
+
+* Net::Protocol
+
+ * Improve `Net::BufferedIO` performance. [[GH-net-protocol-14]]
+
+* Pathname
+
+ * Added `Pathname#lutime`. [[GH-pathname-20]]
+
+* Socket
+
+ * Added the following constants for supported platforms.
+ * `SO_INCOMING_CPU`
+ * `SO_INCOMING_NAPI_ID`
+ * `SO_RTABLE`
+ * `SO_SETFIB`
+ * `SO_USER_COOKIE`
+ * `TCP_KEEPALIVE`
+ * `TCP_CONNECTION_INFO`
+
* SyntaxSuggest
- * The feature of `syntax_suggest` formerly `dead_end` is integrated in Ruby.
- [[Feature #18159]]
+ * The feature of `syntax_suggest` formerly `dead_end` is integrated in Ruby.
+ [[Feature #18159]]
+
+* UNIXSocket
+
+ * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add
+ support for File.socket? and File::Stat#socket? where possible.
+ [[Feature #19135]]
* The following default gems are updated.
- * RubyGems 3.4.0.dev
- * bigdecimal 3.1.2
- * bundler 2.4.0.dev
- * cgi 0.3.2
- * date 3.2.3
- * error_highlight 0.4.0
- * etc 1.4.0
- * io-console 0.5.11
- * io-nonblock 0.1.1
- * io-wait 0.3.0.pre
- * ipaddr 1.2.4
- * json 2.6.2
- * logger 1.5.1
- * net-http 0.2.2
- * net-protocol 0.1.3
+
+ * RubyGems 3.4.1
+ * abbrev 0.1.1
+ * benchmark 0.2.1
+ * bigdecimal 3.1.3
+ * bundler 2.4.1
+ * cgi 0.3.6
+ * csv 3.2.6
+ * date 3.3.3
+ * delegate 0.3.0
+ * did_you_mean 1.6.3
+ * digest 3.1.1
+ * drb 2.1.1
+ * english 0.7.2
+ * erb 4.0.2
+ * error_highlight 0.5.1
+ * etc 1.4.2
+ * fcntl 1.0.2
+ * fiddle 1.1.1
+ * fileutils 1.7.0
+ * forwardable 1.3.3
+ * getoptlong 0.2.0
+ * io-console 0.6.0
+ * io-nonblock 0.2.0
+ * io-wait 0.3.0
+ * ipaddr 1.2.5
+ * irb 1.6.2
+ * json 2.6.3
+ * logger 1.5.3
+ * mutex_m 0.1.2
+ * net-http 0.4.0
+ * net-protocol 0.2.1
+ * nkf 0.1.2
+ * open-uri 0.3.0
+ * open3 0.1.2
+ * openssl 3.1.0
+ * optparse 0.3.1
* ostruct 0.5.5
- * psych 5.0.0.dev
- * reline 0.3.1
- * securerandom 0.2.0
- * stringio 3.0.3
- * syntax_suggest 0.0.1
- * timeout 0.3.0
+ * pathname 0.2.1
+ * pp 0.4.0
+ * pstore 0.1.2
+ * psych 5.0.1
+ * racc 1.6.2
+ * rdoc 6.5.0
+ * readline-ext 0.1.5
+ * reline 0.3.2
+ * resolv 0.2.2
+ * resolv-replace 0.1.1
+ * securerandom 0.2.2
+ * set 1.0.3
+ * stringio 3.0.4
+ * strscan 3.0.5
+ * syntax_suggest 1.0.2
+ * syslog 0.1.1
+ * tempfile 0.1.3
+ * time 0.2.1
+ * timeout 0.3.1
+ * tmpdir 0.1.3
+ * tsort 0.1.1
+ * un 0.2.1
+ * uri 0.12.0
+ * weakref 0.1.2
+ * win32ole 1.8.9
+ * yaml 0.2.1
+ * zlib 3.0.0
+
* The following bundled gems are updated.
+
* minitest 5.16.3
- * net-imap 0.2.3
- * rbs 2.6.0
+ * power_assert 2.0.3
+ * test-unit 3.5.7
+ * net-ftp 0.2.0
+ * net-imap 0.3.4
+ * net-pop 0.1.2
+ * net-smtp 0.3.3
+ * rbs 2.8.2
* typeprof 0.21.3
- * debug 1.6.2
-* The following default gems are now bundled gems.
+ * debug 1.7.1
+
+See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/logger/releases) or changelog for details of the default gems or bundled gems.
+
+## Supported platforms
+
+* WebAssembly/WASI is added. See [wasm/README.md] and [ruby.wasm] for more details. [[Feature #18462]]
## Compatibility issues
-Note: Excluding feature bug fixes.
+* `String#to_c` currently treat a sequence of underscores as an end of Complex
+ string. [[Bug #19087]]
+
+* Now `ENV.clone` raises `TypeError` as well as `ENV.dup` [[Bug #17767]]
### Removed constants
@@ -216,15 +592,77 @@ The following deprecated methods are removed.
[[Feature #16131]]
* `Kernel#trust`, `Kernel#untrust`, `Kernel#untrusted?`
[[Feature #16131]]
+* `Method#public?`, `Method#private?`, `Method#protected?`,
+ `UnboundMethod#public?`, `UnboundMethod#private?`, `UnboundMethod#protected?`
+ [[Bug #18729]] [[Bug #18751]] [[Bug #18435]]
+
+### Source code incompatibility of extension libraries
+
+* Extension libraries provide PRNG, subclasses of Random, need updates.
+ See [PRNG update] below for more information. [[Bug #19100]]
+
+### Error printer
+
+* Ruby no longer escapes control characters and backslashes in an
+ error message. [[Feature #18367]]
+
+### Constant lookup when defining a class/module
+
+* When defining a class/module directly under the Object class by class/module
+ statement, if there is already a class/module defined by `Module#include`
+ with the same name, the statement was handled as "open class" in Ruby 3.1 or before.
+ Since Ruby 3.2, a new class is defined instead. [[Feature #18832]]
## Stdlib compatibility issues
-* `Psych` no longer bundles libyaml sources.
- Users need to install the libyaml library themselves via the package
- system. [[Feature #18571]]
+* Psych no longer bundles libyaml sources.
+ And also Fiddle no longer bundles libffi sources.
+ Users need to install the libyaml/libffi library themselves via the package
+ manager like apt, yum, brew, etc.
+
+ Psych and fiddle supported the static build with specific version of libyaml
+ and libffi sources. You can build psych with libyaml-0.2.5 like this.
+
+ ```bash
+ $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
+ ```
+
+ And you can build fiddle with libffi-3.4.4 like this.
+
+ ```bash
+ $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
+ ```
+
+ [[Feature #18571]]
+
+* Check cookie name/path/domain characters in `CGI::Cookie`. [[CVE-2021-33621]]
+
+* `URI.parse` return empty string in host instead of nil. [[sec-156615]]
## C API updates
+### Updated C APIs
+
+The following APIs are updated.
+
+* PRNG update
+
+ `rb_random_interface_t` in ruby/random.h updated and versioned.
+ Extension libraries which use this interface and built for older
+ versions need to rebuild with adding `init_int32` function.
+
+### Added C APIs
+
+* `VALUE rb_hash_new_capa(long capa)` was added to created hashes with the desired capacity.
+* `rb_internal_thread_add_event_hook` and `rb_internal_thread_add_event_hook` were added to instrument threads scheduling.
+ The following events are available:
+ * `RUBY_INTERNAL_THREAD_EVENT_STARTED`
+ * `RUBY_INTERNAL_THREAD_EVENT_READY`
+ * `RUBY_INTERNAL_THREAD_EVENT_RESUMED`
+ * `RUBY_INTERNAL_THREAD_EVENT_SUSPENDED`
+ * `RUBY_INTERNAL_THREAD_EVENT_EXITED`
+* `rb_debug_inspector_current_depth` and `rb_debug_inspector_frame_depth` are added for debuggers.
+
### Removed C APIs
The following deprecated APIs are removed.
@@ -234,56 +672,149 @@ The following deprecated APIs are removed.
## Implementation improvements
-* Fixed several race conditions in `Kernel#autoload`. [[Bug #18782]]
+* Fixed several race conditions in Kernel#autoload. [[Bug #18782]]
+* Cache invalidation for expressions referencing constants is now
+ more fine-grained. `RubyVM.stat(:global_constant_state)` was
+ removed because it was closely tied to the previous caching scheme
+ where setting any constant invalidates all caches in the system.
+ New keys, `:constant_cache_invalidations` and `:constant_cache_misses`,
+ were introduced to help with use cases for `:global_constant_state`.
+ [[Feature #18589]]
+* The cache-based optimization for Regexp matching is introduced.
+ [[Feature #19104]]
+* [Variable Width Allocation](https://shopify.engineering/ruby-variable-width-allocation)
+ is now enabled by default. [[Feature #18239]]
+* Added a new instance variable caching mechanism, called object shapes, which
+ improves inline cache hits for most objects and allows us to generate very
+ efficient JIT code. Objects whose instance variables are defined in a
+ consistent order will see the most performance benefits.
+ [[Feature #18776]]
+* Speed up marking instruction sequences by using a bitmap to find "markable"
+ objects. This change results in faster major collections.
+ [[Feature #18875]]
## JIT
+### YJIT
+
+* YJIT is no longer experimental
+ * Has been tested on production workloads for over a year and proven to be quite stable.
+* YJIT now supports both x86-64 and arm64/aarch64 CPUs on Linux, MacOS, BSD and other UNIX platforms.
+ * This release brings support for Mac M1/M2, AWS Graviton and Raspberry Pi 4.
+* Building YJIT now requires Rust 1.58.0+. [[Feature #18481]]
+ * In order to ensure that CRuby is built with YJIT, please install `rustc` >= 1.58.0
+ before running `./configure`
+ * Please reach out to the YJIT team should you run into any issues.
+* Physical memory for JIT code is lazily allocated. Unlike Ruby 3.1,
+ the RSS of a Ruby process is minimized because virtual memory pages
+ allocated by `--yjit-exec-mem-size` will not be mapped to physical
+ memory pages until actually utilized by JIT code.
+* Introduce Code GC that frees all code pages when the memory consumption
+ by JIT code reaches `--yjit-exec-mem-size`.
+ * `RubyVM::YJIT.runtime_stats` returns Code GC metrics in addition to
+ existing `inline_code_size` and `outlined_code_size` keys:
+ `code_gc_count`, `live_page_count`, `freed_page_count`, and `freed_code_size`.
+* Most of the statistics produced by `RubyVM::YJIT.runtime_stats` are now available in release builds.
+ * Simply run ruby with `--yjit-stats` to compute and dump stats (incurs some run-time overhead).
+* YJIT is now optimized to take advantage of object shapes. [[Feature #18776]]
+* Take advantage of finer-grained constant invalidation to invalidate less code when defining new constants. [[Feature #18589]]
+* The default `--yjit-exec-mem-size` is changed to 64 (MiB).
+* The default `--yjit-call-threshold` is changed to 30.
+
### MJIT
-### YJIT: New experimental in-process JIT compiler
-
-## Static analysis
-
-### RBS
-
-### TypeProf
-
-## Debugger
-
-## error_highlight
-
-## IRB Autocomplete and Document Display
-
-## Miscellaneous changes
-
-[Feature #12005]: https://bugs.ruby-lang.org/issues/12005
-[Feature #12655]: https://bugs.ruby-lang.org/issues/12655
-[Feature #12737]: https://bugs.ruby-lang.org/issues/12737
-[Feature #13110]: https://bugs.ruby-lang.org/issues/13110
-[Feature #14332]: https://bugs.ruby-lang.org/issues/14332
-[Feature #15231]: https://bugs.ruby-lang.org/issues/15231
-[Feature #15357]: https://bugs.ruby-lang.org/issues/15357
-[Bug #15928]: https://bugs.ruby-lang.org/issues/15928
-[Feature #16131]: https://bugs.ruby-lang.org/issues/16131
-[Bug #16466]: https://bugs.ruby-lang.org/issues/16466
-[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
-[Bug #16889]: https://bugs.ruby-lang.org/issues/16889
-[Bug #16908]: https://bugs.ruby-lang.org/issues/16908
-[Feature #16989]: https://bugs.ruby-lang.org/issues/16989
-[Feature #17351]: https://bugs.ruby-lang.org/issues/17351
-[Feature #17391]: https://bugs.ruby-lang.org/issues/17391
-[Bug #17545]: https://bugs.ruby-lang.org/issues/17545
-[Feature #17881]: https://bugs.ruby-lang.org/issues/17881
-[Feature #18037]: https://bugs.ruby-lang.org/issues/18037
-[Feature #18159]: https://bugs.ruby-lang.org/issues/18159
-[Feature #18351]: https://bugs.ruby-lang.org/issues/18351
-[Bug #18487]: https://bugs.ruby-lang.org/issues/18487
-[Feature #18571]: https://bugs.ruby-lang.org/issues/18571
-[Feature #18585]: https://bugs.ruby-lang.org/issues/18585
-[Feature #18598]: https://bugs.ruby-lang.org/issues/18598
-[Bug #18625]: https://bugs.ruby-lang.org/issues/18625
-[Bug #18633]: https://bugs.ruby-lang.org/issues/18633
-[Feature #18685]: https://bugs.ruby-lang.org/issues/18685
-[Bug #18782]: https://bugs.ruby-lang.org/issues/18782
-[Feature #18788]: https://bugs.ruby-lang.org/issues/18788
-[Feature #18809]: https://bugs.ruby-lang.org/issues/18809
+* The MJIT compiler is re-implemented in Ruby as `ruby_vm/mjit/compiler`.
+* MJIT compiler is executed under a forked Ruby process instead of
+ doing it in a native thread called MJIT worker. [[Feature #18968]]
+ * As a result, Microsoft Visual Studio (MSWIN) is no longer supported.
+* MinGW is no longer supported. [[Feature #18824]]
+* Rename `--mjit-min-calls` to `--mjit-call-threshold`.
+* Change default `--mjit-max-cache` back from 10000 to 100.
+
+[Feature #12005]: https://bugs.ruby-lang.org/issues/12005
+[Feature #12084]: https://bugs.ruby-lang.org/issues/12084
+[Feature #12655]: https://bugs.ruby-lang.org/issues/12655
+[Feature #12737]: https://bugs.ruby-lang.org/issues/12737
+[Feature #13110]: https://bugs.ruby-lang.org/issues/13110
+[Feature #14332]: https://bugs.ruby-lang.org/issues/14332
+[Feature #15231]: https://bugs.ruby-lang.org/issues/15231
+[Feature #15357]: https://bugs.ruby-lang.org/issues/15357
+[Bug #15928]: https://bugs.ruby-lang.org/issues/15928
+[Feature #16122]: https://bugs.ruby-lang.org/issues/16122
+[Feature #16131]: https://bugs.ruby-lang.org/issues/16131
+[Bug #16466]: https://bugs.ruby-lang.org/issues/16466
+[Feature #16663]: https://bugs.ruby-lang.org/issues/16663
+[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
+[Bug #16889]: https://bugs.ruby-lang.org/issues/16889
+[Bug #16908]: https://bugs.ruby-lang.org/issues/16908
+[Feature #16989]: https://bugs.ruby-lang.org/issues/16989
+[Feature #17351]: https://bugs.ruby-lang.org/issues/17351
+[Feature #17391]: https://bugs.ruby-lang.org/issues/17391
+[Bug #17545]: https://bugs.ruby-lang.org/issues/17545
+[Bug #17767]: https://bugs.ruby-lang.org/issues/17767
+[Feature #17837]: https://bugs.ruby-lang.org/issues/17837
+[Feature #17881]: https://bugs.ruby-lang.org/issues/17881
+[Feature #18033]: https://bugs.ruby-lang.org/issues/18033
+[Feature #18159]: https://bugs.ruby-lang.org/issues/18159
+[Feature #18239]: https://bugs.ruby-lang.org/issues/18239#note-17
+[Feature #18351]: https://bugs.ruby-lang.org/issues/18351
+[Feature #18367]: https://bugs.ruby-lang.org/issues/18367
+[Bug #18435]: https://bugs.ruby-lang.org/issues/18435
+[Feature #18462]: https://bugs.ruby-lang.org/issues/18462
+[Feature #18481]: https://bugs.ruby-lang.org/issues/18481
+[Bug #18487]: https://bugs.ruby-lang.org/issues/18487
+[Feature #18564]: https://bugs.ruby-lang.org/issues/18564
+[Feature #18571]: https://bugs.ruby-lang.org/issues/18571
+[Feature #18585]: https://bugs.ruby-lang.org/issues/18585
+[Feature #18589]: https://bugs.ruby-lang.org/issues/18589
+[Feature #18595]: https://bugs.ruby-lang.org/issues/18595
+[Feature #18598]: https://bugs.ruby-lang.org/issues/18598
+[Bug #18625]: https://bugs.ruby-lang.org/issues/18625
+[Feature #18630]: https://bugs.ruby-lang.org/issues/18630
+[Bug #18633]: https://bugs.ruby-lang.org/issues/18633
+[Feature #18639]: https://bugs.ruby-lang.org/issues/18639
+[Feature #18685]: https://bugs.ruby-lang.org/issues/18685
+[Bug #18729]: https://bugs.ruby-lang.org/issues/18729
+[Bug #18751]: https://bugs.ruby-lang.org/issues/18751
+[Feature #18774]: https://bugs.ruby-lang.org/issues/18774
+[Feature #18776]: https://bugs.ruby-lang.org/issues/18776
+[Bug #18782]: https://bugs.ruby-lang.org/issues/18782
+[Feature #18788]: https://bugs.ruby-lang.org/issues/18788
+[Feature #18798]: https://bugs.ruby-lang.org/issues/18798
+[Feature #18809]: https://bugs.ruby-lang.org/issues/18809
+[Feature #18821]: https://bugs.ruby-lang.org/issues/18821
+[Feature #18822]: https://bugs.ruby-lang.org/issues/18822
+[Feature #18824]: https://bugs.ruby-lang.org/issues/18824
+[Feature #18832]: https://bugs.ruby-lang.org/issues/18832
+[Feature #18875]: https://bugs.ruby-lang.org/issues/18875
+[Feature #18925]: https://bugs.ruby-lang.org/issues/18925
+[Feature #18944]: https://bugs.ruby-lang.org/issues/18944
+[Feature #18949]: https://bugs.ruby-lang.org/issues/18949
+[Feature #18968]: https://bugs.ruby-lang.org/issues/18968
+[Feature #19008]: https://bugs.ruby-lang.org/issues/19008
+[Feature #19013]: https://bugs.ruby-lang.org/issues/19013
+[Feature #19026]: https://bugs.ruby-lang.org/issues/19026
+[Feature #19036]: https://bugs.ruby-lang.org/issues/19036
+[Feature #19060]: https://bugs.ruby-lang.org/issues/19060
+[Feature #19070]: https://bugs.ruby-lang.org/issues/19070
+[Feature #19071]: https://bugs.ruby-lang.org/issues/19071
+[Feature #19078]: https://bugs.ruby-lang.org/issues/19078
+[Bug #19087]: https://bugs.ruby-lang.org/issues/19087
+[Bug #19100]: https://bugs.ruby-lang.org/issues/19100
+[Feature #19104]: https://bugs.ruby-lang.org/issues/19104
+[Feature #19135]: https://bugs.ruby-lang.org/issues/19135
+[Feature #19138]: https://bugs.ruby-lang.org/issues/19138
+[Feature #19194]: https://bugs.ruby-lang.org/issues/19194
+[Molinillo]: https://github.com/CocoaPods/Molinillo
+[PubGrub]: https://github.com/jhawthorn/pub_grub
+[GH-net-protocol-14]: https://github.com/ruby/net-protocol/pull/14
+[GH-pathname-20]: https://github.com/ruby/pathname/pull/20
+[GH-6791]: https://github.com/ruby/ruby/pull/6791
+[GH-6868]: https://github.com/ruby/ruby/pull/6868
+[GH-rubygems-4475]: https://github.com/rubygems/rubygems/pull/4475
+[GH-rubygems-6149]: https://github.com/rubygems/rubygems/pull/6149
+[GH-rubygems-6167]: https://github.com/rubygems/rubygems/pull/6167
+[sec-156615]: https://hackerone.com/reports/156615
+[CVE-2021-33621]: https://www.ruby-lang.org/en/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/
+[wasm/README.md]: https://github.com/ruby/ruby/blob/master/wasm/README.md
+[ruby.wasm]: https://github.com/ruby/ruby.wasm
diff --git a/README.ja.md b/README.ja.md
index bb69c09055..93c0131690 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -4,7 +4,6 @@
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-[![Cirrus Status](https://api.cirrus-ci.com/github/ruby/ruby.svg)](https://cirrus-ci.com/github/ruby/ruby/master)
# Rubyとは
diff --git a/README.md b/README.md
index da2bbbdd22..c445448c71 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-[![Cirrus Status](https://api.cirrus-ci.com/github/ruby/ruby.svg)](https://cirrus-ci.com/github/ruby/ruby/master)
# What is Ruby?
@@ -46,6 +45,10 @@ to see the list of branches:
You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source)
if you are a committer.
+## How to build
+
+see [Building Ruby](doc/contributing/building_ruby.md)
+
## Ruby home page
https://www.ruby-lang.org/
diff --git a/addr2line.c b/addr2line.c
index fe4ad84423..e5f25293e2 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -159,12 +159,15 @@ typedef struct obj_info {
struct dwarf_section debug_info;
struct dwarf_section debug_line;
struct dwarf_section debug_ranges;
+ struct dwarf_section debug_str_offsets;
+ struct dwarf_section debug_addr;
struct dwarf_section debug_rnglists;
struct dwarf_section debug_str;
+ struct dwarf_section debug_line_str;
struct obj_info *next;
} obj_info_t;
-#define DWARF_SECTION_COUNT 6
+#define DWARF_SECTION_COUNT 9
static struct dwarf_section *
obj_dwarf_section_at(obj_info_t *obj, int n)
@@ -174,8 +177,11 @@ obj_dwarf_section_at(obj_info_t *obj, int n)
&obj->debug_info,
&obj->debug_line,
&obj->debug_ranges,
+ &obj->debug_str_offsets,
+ &obj->debug_addr,
&obj->debug_rnglists,
- &obj->debug_str
+ &obj->debug_str,
+ &obj->debug_line_str
};
if (n < 0 || DWARF_SECTION_COUNT <= n) {
abort();
@@ -248,39 +254,51 @@ get_nth_dirname(unsigned long dir, const char *p)
return p;
}
+static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index);
+
static void
-fill_filename(int file, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
+fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
{
int i;
const char *p = filenames;
const char *filename;
unsigned long dir;
- for (i = 1; i <= file; i++) {
- filename = p;
- if (!*p) {
- /* Need to output binary file name? */
- kprintf("Unexpected file number %d in %s at %tx\n",
- file, binary_filename, filenames - obj->mapped);
- return;
- }
- while (*p) p++;
- p++;
- dir = uleb128(&p);
- /* last modified. */
- uleb128(&p);
- /* size of the file. */
- uleb128(&p);
-
- if (i == file) {
- line->filename = filename;
- line->dirname = get_nth_dirname(dir, include_directories);
- }
+ if (version >= 5) {
+ const char *path;
+ uint64_t directory_index = -1;
+ parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index);
+ line->filename = path;
+ parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL);
+ line->dirname = path;
+ }
+ else {
+ for (i = 1; i <= file; i++) {
+ filename = p;
+ if (!*p) {
+ /* Need to output binary file name? */
+ kprintf("Unexpected file number %d in %s at %tx\n",
+ file, binary_filename, filenames - obj->mapped);
+ return;
+ }
+ while (*p) p++;
+ p++;
+ dir = uleb128(&p);
+ /* last modified. */
+ uleb128(&p);
+ /* size of the file. */
+ uleb128(&p);
+
+ if (i == file) {
+ line->filename = filename;
+ line->dirname = get_nth_dirname(dir, include_directories);
+ }
+ }
}
}
static void
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
- const char *include_directories, const char *filenames,
+ uint8_t format, uint16_t version, const char *include_directories, const char *filenames,
obj_info_t *obj, line_info_t *lines, int offset)
{
int i;
@@ -290,7 +308,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
/* We assume one line code doesn't result >100 bytes of native code.
We may want more reliable way eventually... */
if (addr < a && a < addr + 100) {
- fill_filename(file, include_directories, filenames, &lines[i], obj);
+ fill_filename(file, format, version, include_directories, filenames, &lines[i], obj);
lines[i].line = line;
}
}
@@ -315,7 +333,7 @@ struct LineNumberProgramHeader {
};
static int
-parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
+parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header)
{
const char *p = *pp;
header->unit_length = *(uint32_t *)p;
@@ -332,7 +350,13 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
header->version = *(uint16_t *)p;
p += sizeof(uint16_t);
- if (header->version > 4) return -1;
+ if (header->version > 5) return -1;
+
+ if (header->version >= 5) {
+ /* address_size = *(uint8_t *)p++; */
+ /* segment_selector_size = *(uint8_t *)p++; */
+ p += 2;
+ }
header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p;
p += header->format;
@@ -353,20 +377,27 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
/* header->standard_opcode_lengths = (uint8_t *)p - 1; */
p += header->opcode_base - 1;
- header->include_directories = p;
+ if (header->version >= 5) {
+ header->include_directories = p;
+ p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL);
+ header->filenames = p;
+ }
+ else {
+ header->include_directories = p;
- /* temporary measure for compress-debug-sections */
- if (p >= header->cu_end) return -1;
+ /* temporary measure for compress-debug-sections */
+ if (p >= header->cu_end) return -1;
- /* skip include directories */
- while (*p) {
- p = memchr(p, '\0', header->cu_end - p);
- if (!p) return -1;
- p++;
- }
- p++;
+ /* skip include directories */
+ while (*p) {
+ p = memchr(p, '\0', header->cu_end - p);
+ if (!p) return -1;
+ p++;
+ }
+ p++;
- header->filenames = p;
+ header->filenames = p;
+ }
*pp = header->cu_start;
@@ -392,13 +423,15 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
/* int epilogue_begin = 0; */
/* unsigned int isa = 0; */
- if (parse_debug_line_header(&p, &header))
+ if (parse_debug_line_header(obj, &p, &header))
return -1;
is_stmt = header.default_is_stmt;
#define FILL_LINE() \
do { \
fill_line(num_traces, traces, addr, file, line, \
+ header.format, \
+ header.version, \
header.include_directories, \
header.filenames, \
obj, lines, offset); \
@@ -827,16 +860,23 @@ enum {
VAL_cstr = 1,
VAL_data = 2,
VAL_uint = 3,
- VAL_int = 4
+ VAL_int = 4,
+ VAL_addr = 5
};
# define ABBREV_TABLE_SIZE 256
typedef struct {
obj_info_t *obj;
const char *file;
+ uint8_t current_version;
const char *current_cu;
uint64_t current_low_pc;
+ uint64_t current_str_offsets_base;
+ uint64_t current_addr_base;
+ uint64_t current_rnglists_base;
const char *debug_line_cu_end;
+ uint8_t debug_line_format;
+ uint16_t debug_line_version;
const char *debug_line_files;
const char *debug_line_directories;
const char *p;
@@ -861,6 +901,7 @@ typedef struct {
const char *ptr;
uint64_t uint64;
int64_t int64;
+ uint64_t addr_idx;
} as;
uint64_t off;
uint64_t at;
@@ -869,8 +910,11 @@ typedef struct {
int type;
} DebugInfoValue;
-/* TODO: Big Endian */
+#if defined(WORDS_BIGENDIAN)
+#define MERGE_2INTS(a,b,sz) (((uint64_t)(a)<<sz)|(b))
+#else
#define MERGE_2INTS(a,b,sz) (((uint64_t)(b)<<sz)|(a))
+#endif
static uint16_t
get_uint16(const uint8_t *p)
@@ -973,6 +1017,9 @@ debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj)
reader->pend = obj->debug_info.ptr + obj->debug_info.size;
reader->debug_line_cu_end = obj->debug_line.ptr;
reader->current_low_pc = 0;
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
}
static void
@@ -1017,10 +1064,12 @@ di_read_debug_line_cu(DebugInfoReader *reader)
struct LineNumberProgramHeader header;
p = (const char *)reader->debug_line_cu_end;
- if (parse_debug_line_header(&p, &header))
+ if (parse_debug_line_header(reader->obj, &p, &header))
return -1;
reader->debug_line_cu_end = (char *)header.cu_end;
+ reader->debug_line_format = header.format;
+ reader->debug_line_version = header.version;
reader->debug_line_directories = (char *)header.include_directories;
reader->debug_line_files = (char *)header.filenames;
@@ -1028,6 +1077,13 @@ di_read_debug_line_cu(DebugInfoReader *reader)
}
static void
+set_addr_idx_value(DebugInfoValue *v, uint64_t n)
+{
+ v->as.addr_idx = n;
+ v->type = VAL_addr;
+}
+
+static void
set_uint_value(DebugInfoValue *v, uint64_t n)
{
v->as.uint64 = n;
@@ -1074,19 +1130,39 @@ get_cstr_value(DebugInfoValue *v)
}
}
+static const char *
+resolve_strx(DebugInfoReader *reader, uint64_t idx)
+{
+ const char *p = reader->obj->debug_str_offsets.ptr + reader->current_str_offsets_base;
+ uint64_t off;
+ if (reader->format == 4) {
+ off = ((uint32_t *)p)[idx];
+ }
+ else {
+ off = ((uint64_t *)p)[idx];
+ }
+ return reader->obj->debug_str.ptr + off;
+}
+
+static void
+debug_info_reader_read_addr_value(DebugInfoReader *reader, DebugInfoValue *v)
+{
+ if (reader->address_size == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->address_size == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown address_size:%d", reader->address_size);
+ abort();
+ }
+}
+
static void
debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v)
{
switch (form) {
case DW_FORM_addr:
- if (reader->address_size == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->address_size == 8) {
- set_uint_value(v, read_uint64(&reader->p));
- } else {
- fprintf(stderr,"unknown address_size:%d", reader->address_size);
- abort();
- }
+ debug_info_reader_read_addr_value(reader, v);
break;
case DW_FORM_block2:
v->size = read_uint16(&reader->p);
@@ -1138,13 +1214,19 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, read_uleb128(reader));
break;
case DW_FORM_ref_addr:
- if (reader->format == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->format == 8) {
- set_uint_value(v, read_uint64(&reader->p));
+ if (reader->current_version <= 2) {
+ // DWARF Version 2 specifies that references have
+ // the same size as an address on the target system
+ debug_info_reader_read_addr_value(reader, v);
} else {
- fprintf(stderr,"unknown format:%d", reader->format);
- abort();
+ if (reader->format == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->format == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown format:%d", reader->format);
+ abort();
+ }
}
break;
case DW_FORM_ref1:
@@ -1186,11 +1268,10 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, 1);
break;
case DW_FORM_strx:
- set_uint_value(v, uleb128(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, uleb128(&reader->p)));
break;
case DW_FORM_addrx:
- /* TODO: read .debug_addr */
- set_uint_value(v, uleb128(&reader->p));
+ set_addr_idx_value(v, uleb128(&reader->p));
break;
case DW_FORM_ref_sup4:
set_uint_value(v, read_uint32(&reader->p));
@@ -1205,8 +1286,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
reader->p += v->size;
break;
case DW_FORM_line_strp:
- set_uint_value(v, read_uint(reader));
- /* *p = reader->file + reader->line->sh_offset + ret; */
+ set_cstrp_value(v, reader->obj->debug_line_str.ptr, read_uint(reader));
break;
case DW_FORM_ref_sig8:
set_uint_value(v, read_uint64(&reader->p));
@@ -1224,28 +1304,28 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, read_uint64(&reader->p));
break;
case DW_FORM_strx1:
- set_uint_value(v, read_uint8(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint8(&reader->p)));
break;
case DW_FORM_strx2:
- set_uint_value(v, read_uint16(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint16(&reader->p)));
break;
case DW_FORM_strx3:
- set_uint_value(v, read_uint24(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint24(&reader->p)));
break;
case DW_FORM_strx4:
- set_uint_value(v, read_uint32(&reader->p));
+ set_cstr_value(v, resolve_strx(reader, read_uint32(&reader->p)));
break;
case DW_FORM_addrx1:
- set_uint_value(v, read_uint8(&reader->p));
+ set_addr_idx_value(v, read_uint8(&reader->p));
break;
case DW_FORM_addrx2:
- set_uint_value(v, read_uint16(&reader->p));
+ set_addr_idx_value(v, read_uint16(&reader->p));
break;
case DW_FORM_addrx3:
- set_uint_value(v, read_uint24(&reader->p));
+ set_addr_idx_value(v, read_uint24(&reader->p));
break;
case DW_FORM_addrx4:
- set_uint_value(v, read_uint32(&reader->p));
+ set_addr_idx_value(v, read_uint32(&reader->p));
break;
case 0:
goto fail;
@@ -1373,6 +1453,76 @@ di_skip_records(DebugInfoReader *reader)
}
}
+typedef struct addr_header {
+ const char *ptr;
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ /* uint8_t segment_selector_size; */
+} addr_header_t;
+
+static void
+addr_header_init(obj_info_t *obj, addr_header_t *header) {
+ const char *p = obj->debug_addr.ptr;
+
+ header->ptr = p;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+}
+
+static uint64_t
+read_addr(addr_header_t *header, uint64_t addr_base, uint64_t idx) {
+ if (header->address_size == 4) {
+ return ((uint32_t*)(header->ptr + addr_base))[idx];
+ }
+ else {
+ return ((uint64_t*)(header->ptr + addr_base))[idx];
+ }
+}
+
+typedef struct rnglists_header {
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ uint32_t offset_entry_count;
+} rnglists_header_t;
+
+static void
+rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) {
+ const char *p = obj->debug_rnglists.ptr;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+ header->offset_entry_count = *(uint32_t *)p;
+}
+
typedef struct {
uint64_t low_pc;
uint64_t high_pc;
@@ -1383,24 +1533,31 @@ typedef struct {
} ranges_t;
static void
-ranges_set(ranges_t *ptr, DebugInfoValue *v)
+ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_t addr_base)
{
+ uint64_t n = 0;
+ if (v->type == VAL_uint) {
+ n = v->as.uint64;
+ }
+ else if (v->type == VAL_addr) {
+ n = read_addr(addr_header, addr_base, v->as.addr_idx);
+ }
switch (v->at) {
case DW_AT_low_pc:
- ptr->low_pc = v->as.uint64;
+ ptr->low_pc = n;
ptr->low_pc_set = true;
break;
case DW_AT_high_pc:
if (v->form == DW_FORM_addr) {
- ptr->high_pc = v->as.uint64;
+ ptr->high_pc = n;
}
else {
- ptr->high_pc = ptr->low_pc + v->as.uint64;
+ ptr->high_pc = ptr->low_pc + n;
}
ptr->high_pc_set = true;
break;
case DW_AT_ranges:
- ptr->ranges = v->as.uint64;
+ ptr->ranges = n;
ptr->ranges_set = true;
break;
}
@@ -1422,7 +1579,7 @@ read_dw_form_addr(DebugInfoReader *reader, const char **ptr)
}
static uintptr_t
-ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
+ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header)
{
if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) {
@@ -1437,8 +1594,21 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
const char *p;
uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc;
bool base_valid = true;
- if (reader->obj->debug_rnglists.ptr) {
- p = reader->obj->debug_rnglists.ptr + ptr->ranges;
+ if (reader->current_version >= 5) {
+ if (rnglists_header->offset_entry_count == 0) {
+ // DW_FORM_sec_offset
+ p = reader->obj->debug_rnglists.ptr + ptr->ranges + reader->current_rnglists_base;
+ }
+ else {
+ // DW_FORM_rnglistx
+ const char *offset_array = reader->obj->debug_rnglists.ptr + reader->current_rnglists_base;
+ if (rnglists_header->format == 4) {
+ p = offset_array + ((uint32_t *)offset_array)[ptr->ranges];
+ }
+ else {
+ p = offset_array + ((uint64_t *)offset_array)[ptr->ranges];
+ }
+ }
for (;;) {
uint8_t rle = read_uint8(&p);
uintptr_t from = 0, to = 0;
@@ -1548,6 +1718,7 @@ di_read_cu(DebugInfoReader *reader)
}
reader->cu_end = reader->p + unit_length;
version = read_uint16(&reader->p);
+ reader->current_version = version;
if (version > 5) {
return -1;
}
@@ -1580,16 +1751,45 @@ di_read_cu(DebugInfoReader *reader)
break;
}
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
+
+ DebugInfoValue low_pc = {{}};
/* enumerate abbrev */
for (;;) {
DebugInfoValue v = {{}};
if (!di_read_record(reader, &v)) break;
switch (v.at) {
case DW_AT_low_pc:
- reader->current_low_pc = v.as.uint64;
+ // clang may output DW_AT_addr_base after DW_AT_low_pc.
+ // We need to resolve the DW_FORM_addr* after DW_AT_addr_base is parsed.
+ low_pc = v;
+ break;
+ case DW_AT_str_offsets_base:
+ reader->current_str_offsets_base = v.as.uint64;
+ break;
+ case DW_AT_addr_base:
+ reader->current_addr_base = v.as.uint64;
+ break;
+ case DW_AT_rnglists_base:
+ reader->current_rnglists_base = v.as.uint64;
break;
}
}
+ // Resolve the DW_FORM_addr of DW_AT_low_pc
+ switch (low_pc.type) {
+ case VAL_uint:
+ reader->current_low_pc = low_pc.as.uint64;
+ break;
+ case VAL_addr:
+ {
+ addr_header_t header;
+ addr_header_init(reader->obj, &header);
+ reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx);
+ }
+ break;
+ }
} while (0);
#endif
return 0;
@@ -1643,6 +1843,13 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
static void
debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
line_info_t *lines, int offset) {
+
+ addr_header_t addr_header = {};
+ addr_header_init(reader->obj, &addr_header);
+
+ rnglists_header_t rnglists_header = {};
+ rnglists_header_init(reader->obj, &rnglists_header);
+
while (reader->p < reader->cu_end) {
DIE die;
ranges_t ranges = {};
@@ -1669,7 +1876,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
line.sname = get_cstr_value(&v);
break;
case DW_AT_call_file:
- fill_filename((int)v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
+ fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
break;
case DW_AT_call_line:
line.line = (int)v.as.uint64;
@@ -1677,7 +1884,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
case DW_AT_low_pc:
case DW_AT_high_pc:
case DW_AT_ranges:
- ranges_set(&ranges, &v);
+ ranges_set(&ranges, &v, &addr_header, reader->current_addr_base);
break;
case DW_AT_declaration:
goto skip_die;
@@ -1694,9 +1901,9 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
for (int i=offset; i < num_traces; i++) {
uintptr_t addr = (uintptr_t)traces[i];
uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr;
- uintptr_t saddr = ranges_include(reader, &ranges, offset);
+ uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header);
if (saddr) {
- /* fprintf(stderr, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
+ /* fprintf(stdout, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
if (lines[i].sname) {
line_info_t *lp = malloc(sizeof(line_info_t));
memcpy(lp, &lines[i], sizeof(line_info_t));
@@ -1715,6 +1922,54 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
}
}
+// This function parses the following attributes of Line Number Program Header in DWARF 5:
+//
+// * directory_entry_format_count
+// * directory_entry_format
+// * directories_count
+// * directories
+//
+// or
+//
+// * file_name_entry_format_count
+// * file_name_entry_format
+// * file_names_count
+// * file_names
+//
+// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx".
+static const char *
+parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) {
+ int i, j;
+ int entry_format_count = *(uint8_t *)p++;
+ const char *entry_format = p;
+
+ /* skip the part of entry_format */
+ for (i = 0; i < entry_format_count * 2; i++) uleb128(&p);
+
+ int entry_count = (int)uleb128(&p);
+
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ reader.format = format;
+ reader.p = p;
+ for (j = 0; j < entry_count; j++) {
+ const char *format = entry_format;
+ for (i = 0; i < entry_format_count; i++) {
+ DebugInfoValue v = {{}};
+ unsigned long dw_lnct = uleb128(&format);
+ unsigned long dw_form = uleb128(&format);
+ debug_info_reader_read_value(&reader, dw_form, &v);
+ if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path)
+ *out_path = v.as.ptr + v.off;
+ if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index)
+ *out_directory_index = v.as.uint64;
+ }
+ if (j == idx) return 0;
+ }
+
+ return reader.p;
+}
+
#ifdef USE_ELF
static unsigned long
uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
@@ -1843,8 +2098,11 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
".debug_info",
".debug_line",
".debug_ranges",
+ ".debug_str_offsets",
+ ".debug_addr",
".debug_rnglists",
- ".debug_str"
+ ".debug_str",
+ ".debug_line_str"
};
for (j=0; j < DWARF_SECTION_COUNT; j++) {
@@ -2100,8 +2358,11 @@ found_mach_header:
"__debug_info",
"__debug_line",
"__debug_ranges",
+ "__debug_str_offsets",
+ "__debug_addr",
"__debug_rnglists",
- "__debug_str"
+ "__debug_str",
+ "__debug_line_str",
};
struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd;
if (strcmp(scmd->segname, "__TEXT") == 0) {
@@ -2299,6 +2560,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
obj_info_t *obj = NULL;
/* 2 is NULL + main executable */
void **dladdr_fbases = (void **)calloc(num_traces+2, sizeof(void *));
+
#ifdef HAVE_MAIN_EXE_PATH
char *main_path = NULL; /* used on printing backtrace */
ssize_t len;
diff --git a/array.c b/array.c
index 793a53f17b..b76e9a64a3 100644
--- a/array.c
+++ b/array.c
@@ -226,8 +226,15 @@ ary_embeddable_p(long capa)
bool
rb_ary_embeddable_p(VALUE ary)
{
- // if the array is shared or a shared root then it's not moveable
- return !(ARY_SHARED_P(ary) || ARY_SHARED_ROOT_P(ary));
+ /* An array cannot be turned embeddable when the array is:
+ * - Shared root: other objects may point to the buffer of this array
+ * so we cannot make it embedded.
+ * - Frozen: this array may also be a shared root without the shared root
+ * flag.
+ * - Shared: we don't want to re-embed an array that points to a shared
+ * root (to save memory).
+ */
+ return !(ARY_SHARED_ROOT_P(ary) || OBJ_FROZEN(ary) || ARY_SHARED_P(ary));
}
size_t
@@ -242,7 +249,7 @@ rb_ary_size_as_embedded(VALUE ary)
real_size = ary_embed_size(ARY_HEAP_CAPA(ary));
}
else {
- real_size = sizeof(struct RString);
+ real_size = sizeof(struct RArray);
}
return real_size;
}
@@ -505,15 +512,19 @@ rb_ary_make_embedded(VALUE ary)
{
assert(rb_ary_embeddable_p(ary));
if (!ARY_EMBED_P(ary)) {
- VALUE *buf = RARRAY_PTR(ary);
- long len = RARRAY_LEN(ary);
+ const VALUE *buf = ARY_HEAP_PTR(ary);
+ long len = ARY_HEAP_LEN(ary);
+ bool was_transient = RARRAY_TRANSIENT_P(ary);
+ // FL_SET_EMBED also unsets the transient flag
FL_SET_EMBED(ary);
ARY_SET_EMBED_LEN(ary, len);
- RARY_TRANSIENT_UNSET(ary);
- memmove(RARRAY_PTR(ary), buf, len * sizeof(VALUE));
- ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ MEMCPY((void *)ARY_EMBED_PTR(ary), (void *)buf, VALUE, len);
+
+ if (!was_transient) {
+ ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ }
}
}
@@ -833,11 +844,11 @@ ary_new(VALUE klass, long capa)
}
else {
ary = ary_alloc_heap(klass);
+ ARY_SET_CAPA(ary, capa);
assert(!ARY_EMBED_P(ary));
ptr = ary_heap_alloc(ary, capa);
ARY_SET_PTR(ary, ptr);
- ARY_SET_CAPA(ary, capa);
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -941,11 +952,11 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
}
else {
ary = ec_ary_alloc_heap(ec, klass);
+ ARY_SET_CAPA(ary, capa);
assert(!ARY_EMBED_P(ary));
ptr = ary_heap_alloc(ary, capa);
ARY_SET_PTR(ary, ptr);
- ARY_SET_CAPA(ary, capa);
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -1052,6 +1063,7 @@ ary_make_shared(VALUE ary)
/* Shared roots cannot be embedded because the reference count
* (refcnt) is stored in as.heap.aux.capa. */
VALUE shared = ary_alloc_heap(0);
+ FL_SET_SHARED_ROOT(shared);
if (ARY_EMBED_P(ary)) {
/* Cannot use ary_heap_alloc because we don't want to allocate
@@ -1070,7 +1082,6 @@ ary_make_shared(VALUE ary)
ARY_SET_LEN(shared, capa);
ary_mem_clear(shared, len, capa - len);
- FL_SET_SHARED_ROOT(shared);
ARY_SET_SHARED_ROOT_REFCNT(shared, 1);
FL_SET_SHARED(ary);
RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
@@ -1344,11 +1355,11 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
return result;
}
else {
+ VALUE shared = ary_make_shared(ary);
+
VALUE result = ary_alloc_heap(klass);
assert(!ARY_EMBED_P(result));
- VALUE shared = ary_make_shared(ary);
-
ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
@@ -1385,7 +1396,7 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
}
long ustep = (step < 0) ? -step : step;
- len = (len + ustep - 1) / ustep;
+ len = roomof(len, ustep);
long i;
long j = offset + ((step > 0) ? 0 : (orig_len - 1));
@@ -2082,7 +2093,7 @@ rb_ary_first(int argc, VALUE *argv, VALUE ary)
*
* If +self+ is empty, returns +nil+.
*
- * When non-negative \Innteger argument +n+ is given,
+ * When non-negative \Integer argument +n+ is given,
* returns the last +n+ elements in a new \Array:
*
* a = [:foo, 'bar', 2]
@@ -3452,7 +3463,6 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
struct ary_sort_data {
VALUE ary;
VALUE receiver;
- struct cmp_opt_data cmp_opt;
};
static VALUE
@@ -3498,15 +3508,15 @@ sort_2(const void *ap, const void *bp, void *dummy)
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
int n;
- if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, Integer)) {
+ if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(INTEGER)) {
if ((long)a > (long)b) return 1;
if ((long)a < (long)b) return -1;
return 0;
}
- if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, String)) {
+ if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(STRING)) {
return rb_str_cmp(a, b);
}
- if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(data->cmp_opt, Float)) {
+ if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(FLOAT)) {
return rb_float_cmp(a, b);
}
@@ -3570,8 +3580,6 @@ rb_ary_sort_bang(VALUE ary)
RBASIC_CLEAR_CLASS(tmp);
data.ary = tmp;
data.receiver = ary;
- data.cmp_opt.opt_methods = 0;
- data.cmp_opt.opt_inited = 0;
RARRAY_PTR_USE(tmp, ptr, {
ruby_qsort(ptr, len, sizeof(VALUE),
rb_block_given_p()?sort_1:sort_2, &data);
@@ -3728,8 +3736,8 @@ rb_ary_bsearch_index(VALUE ary)
const VALUE zero = INT2FIX(0);
switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
case 0: return INT2FIX(mid);
- case 1: smaller = 1; break;
- case -1: smaller = 0;
+ case 1: smaller = 0; break;
+ case -1: smaller = 1;
}
}
else {
@@ -4551,7 +4559,7 @@ take_items(VALUE obj, long n)
if (!NIL_P(result)) return rb_ary_subseq(result, 0, n);
result = rb_ary_new2(n);
args[0] = result; args[1] = (VALUE)n;
- if (rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args) == Qundef)
+ if (UNDEF_P(rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args)))
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
rb_obj_class(obj));
return result;
@@ -5044,7 +5052,7 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary)
ARY_SET_LEN(ary, end);
}
- if (item == Qundef) {
+ if (UNDEF_P(item)) {
VALUE v;
long i;
@@ -5501,7 +5509,7 @@ rb_ary_cmp(VALUE ary1, VALUE ary2)
if (NIL_P(ary2)) return Qnil;
if (ary1 == ary2) return INT2FIX(0);
v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2);
- if (v != Qundef) return v;
+ if (!UNDEF_P(v)) return v;
len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1);
@@ -5583,7 +5591,7 @@ ary_recycle_hash(VALUE hash)
* Related: Array#difference.
*/
-static VALUE
+VALUE
rb_ary_diff(VALUE ary1, VALUE ary2)
{
VALUE ary3;
@@ -6052,7 +6060,6 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax)
static VALUE
rb_ary_max(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
@@ -6064,7 +6071,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
if (rb_block_given_p()) {
for (i = 0; i < RARRAY_LEN(ary); i++) {
v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
result = v;
}
}
@@ -6072,13 +6079,13 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
else if (n > 0) {
result = RARRAY_AREF(ary, 0);
if (n > 1) {
- if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
return ary_max_opt_fixnum(ary, 1, result);
}
- else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
return ary_max_opt_string(ary, 1, result);
}
- else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
return ary_max_opt_float(ary, 1, result);
}
else {
@@ -6086,7 +6093,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
}
}
}
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -6221,7 +6228,6 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin)
static VALUE
rb_ary_min(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
@@ -6233,7 +6239,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
if (rb_block_given_p()) {
for (i = 0; i < RARRAY_LEN(ary); i++) {
v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
result = v;
}
}
@@ -6241,13 +6247,13 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
else if (n > 0) {
result = RARRAY_AREF(ary, 0);
if (n > 1) {
- if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
return ary_min_opt_fixnum(ary, 1, result);
}
- else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
return ary_min_opt_string(ary, 1, result);
}
- else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
return ary_min_opt_float(ary, 1, result);
}
else {
@@ -6255,7 +6261,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
}
}
}
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -8144,7 +8150,7 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z)
{
if (n != 0)
v = rb_fix_plus(LONG2FIX(n), v);
- if (r != Qundef) {
+ if (!UNDEF_P(r)) {
v = rb_rational_plus(r, v);
}
else if (!n && z) {
@@ -8223,7 +8229,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
else if (RB_BIGNUM_TYPE_P(e))
v = rb_big_plus(e, v);
else if (RB_TYPE_P(e, T_RATIONAL)) {
- if (r == Qundef)
+ if (UNDEF_P(r))
r = e;
else
r = rb_rational_plus(r, e);
@@ -8733,7 +8739,7 @@ rb_ary_deconstruct(VALUE ary)
*
* - #pop: Removes and returns the last element.
* - #shift: Removes and returns the first element.
- * - #compact!: Removes all non-+nil+ elements.
+ * - #compact!: Removes all +nil+ elements.
* - #delete: Removes elements equal to a given object.
* - #delete_at: Removes the element at a given offset.
* - #delete_if: Removes elements specified by a given block.
diff --git a/ast.c b/ast.c
index 42d4126a5b..adb7287ed3 100644
--- a/ast.c
+++ b/ast.c
@@ -64,8 +64,8 @@ ast_new_internal(rb_ast_t *ast, const NODE *node)
return obj;
}
-static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines);
-static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines);
+static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
+static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
static VALUE
ast_parse_new(void)
@@ -85,31 +85,33 @@ ast_parse_done(rb_ast_t *ast)
}
static VALUE
-ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines)
+ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- return rb_ast_parse_str(str, keep_script_lines);
+ return rb_ast_parse_str(str, keep_script_lines, error_tolerant, keep_tokens);
}
static VALUE
-rb_ast_parse_str(VALUE str, VALUE keep_script_lines)
+rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
rb_ast_t *ast = 0;
StringValue(str);
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
return ast_parse_done(ast);
}
static VALUE
-ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines)
+ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- return rb_ast_parse_file(path, keep_script_lines);
+ return rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
}
static VALUE
-rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
+rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
VALUE f;
rb_ast_t *ast = 0;
@@ -120,6 +122,8 @@ rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
rb_io_close(f);
return ast_parse_done(ast);
@@ -139,13 +143,15 @@ lex_array(VALUE array, int index)
}
static VALUE
-rb_ast_parse_array(VALUE array, VALUE keep_script_lines)
+rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
rb_ast_t *ast = 0;
array = rb_check_array_type(array);
VALUE vparser = ast_parse_new();
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
return ast_parse_done(ast);
}
@@ -193,7 +199,24 @@ script_lines(VALUE path)
}
static VALUE
-ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines)
+node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
+{
+ int node_id;
+
+ if (!rb_frame_info_p(location)) {
+ rb_raise(rb_eTypeError, "Thread::Backtrace::Location object expected");
+ }
+
+ node_id = rb_get_node_id_from_frame_info(location);
+ if (node_id == -1) {
+ return Qnil;
+ }
+
+ return INT2NUM(node_id);
+}
+
+static VALUE
+ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
VALUE node, lines = Qnil;
const rb_iseq_t *iseq;
@@ -232,13 +255,13 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
}
if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
- node = rb_ast_parse_array(lines, keep_script_lines);
+ node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant, keep_tokens);
}
else if (e_option) {
- node = rb_ast_parse_str(rb_e_script, keep_script_lines);
+ node = rb_ast_parse_str(rb_e_script, keep_script_lines, error_tolerant, keep_tokens);
}
else {
- node = rb_ast_parse_file(path, keep_script_lines);
+ node = rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
}
return node_find(node, node_id);
@@ -645,6 +668,8 @@ node_children(rb_ast_t *ast, const NODE *node)
NEW_CHILD(ast, node->nd_pkwargs),
kwrest);
}
+ case NODE_ERROR:
+ return rb_ary_new_from_node_args(ast, 0);
case NODE_ARGS_AUX:
case NODE_LAST:
break;
@@ -699,6 +724,15 @@ ast_node_last_column(rb_execution_context_t *ec, VALUE self)
}
static VALUE
+ast_node_all_tokens(rb_execution_context_t *ec, VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return rb_ast_tokens(data->ast);
+}
+
+static VALUE
ast_node_inspect(rb_execution_context_t *ec, VALUE self)
{
VALUE str;
diff --git a/ast.rb b/ast.rb
index f866bd23e5..f3f72c747f 100644
--- a/ast.rb
+++ b/ast.rb
@@ -20,21 +20,47 @@
module RubyVM::AbstractSyntaxTree
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse(string) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Parses the given _string_ into an abstract syntax tree,
# returning the root node of that tree.
#
- # SyntaxError is raised if the given _string_ is invalid syntax.
- #
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
- def self.parse string, keep_script_lines: false
- Primitive.ast_s_parse string, keep_script_lines
+ #
+ # If <tt>keep_script_lines: true</tt> option is provided, the text of the parsed
+ # source is associated with nodes and is available via Node#script_lines.
+ #
+ # If <tt>keep_tokens: true</tt> option is provided, Node#tokens are populated.
+ #
+ # SyntaxError is raised if the given _string_ is invalid syntax. To overwrite this
+ # behavior, <tt>error_tolerant: true</tt> can be provided. In this case, the parser
+ # will produce a tree where expressions with syntax errors would be represented by
+ # Node with <tt>type=:ERROR</tt>.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2")
+ # # <internal:ast>:33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError)
+ # # x = 1; p(x; y=2
+ # # ^
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true)
+ # # (SCOPE@1:0-1:15
+ # # tbl: [:x, :y]
+ # # args: nil
+ # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))))
+ # root.children.last.children
+ # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)),
+ # # (ERROR@1:7-1:11),
+ # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))]
+ #
+ # Note that parsing continues even after the errored expresion.
+ #
+ def self.parse string, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Reads the file from _pathname_, then parses it like ::parse,
# returning the root node of the abstract syntax tree.
@@ -44,13 +70,15 @@ module RubyVM::AbstractSyntaxTree
#
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
- def self.parse_file pathname, keep_script_lines: false
- Primitive.ast_s_parse_file pathname, keep_script_lines
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.parse_file pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node
- # RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Returns AST nodes of the given _proc_ or _method_.
#
@@ -63,8 +91,25 @@ module RubyVM::AbstractSyntaxTree
#
# RubyVM::AbstractSyntaxTree.of(method(:hello))
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
- def self.of body, keep_script_lines: false
- Primitive.ast_s_of body, keep_script_lines
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.of body, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens
+ end
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(backtrace_location) -> integer
+ #
+ # Returns the node id for the given backtrace location.
+ #
+ # begin
+ # raise
+ # rescue => e
+ # loc = e.backtrace_locations.first
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
+ # end # => 0
+ def self.node_id_for_backtrace_location backtrace_location
+ Primitive.node_id_for_backtrace_location backtrace_location
end
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
@@ -122,6 +167,47 @@ module RubyVM::AbstractSyntaxTree
end
# call-seq:
+ # node.tokens -> array
+ #
+ # Returns tokens corresponding to the location of the node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ #
+ # Token is an array of:
+ #
+ # - id
+ # - token type
+ # - source code text
+ # - location [ first_lineno, first_column, last_lineno, last_column ]
+ def tokens
+ return nil unless all_tokens
+
+ all_tokens.each_with_object([]) do |token, a|
+ loc = token.last
+ if ([first_lineno, first_column] <=> [loc[0], loc[1]]) <= 0 &&
+ ([last_lineno, last_column] <=> [loc[2], loc[3]]) >= 0
+ a << token
+ end
+ end
+ end
+
+ # call-seq:
+ # node.all_tokens -> array
+ #
+ # Returns all tokens for the input script regardless the receiver node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.children[-1].all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ def all_tokens
+ Primitive.ast_node_all_tokens
+ end
+
+ # call-seq:
# node.children -> array
#
# Returns AST nodes under this one. Each kind of node
diff --git a/benchmark/array_sort_int.yml b/benchmark/array_sort_int.yml
new file mode 100644
index 0000000000..7b9027ebf7
--- /dev/null
+++ b/benchmark/array_sort_int.yml
@@ -0,0 +1,15 @@
+prelude: |
+ ary2 = 2.times.to_a.shuffle
+ ary10 = 10.times.to_a.shuffle
+ ary100 = 100.times.to_a.shuffle
+ ary1000 = 1000.times.to_a.shuffle
+ ary10000 = 10000.times.to_a.shuffle
+
+benchmark:
+ ary2.sort: ary2.sort
+ ary10.sort: ary10.sort
+ ary100.sort: ary100.sort
+ ary1000.sort: ary1000.sort
+ ary10000.sort: ary10000.sort
+
+loop_count: 10000
diff --git a/benchmark/buffer_each.yml b/benchmark/buffer_each.yml
new file mode 100644
index 0000000000..417941104e
--- /dev/null
+++ b/benchmark/buffer_each.yml
@@ -0,0 +1,27 @@
+prelude: |
+ # frozen_string_literal: true
+ Warning[:experimental] = false
+ string = "The quick brown fox jumped over the lazy dog."
+ array = string.bytes
+ buffer = IO::Buffer.for(string)
+benchmark:
+ string.each_byte: |
+ upcased = String.new
+ string.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
+ array.each: |
+ upcased = String.new
+ array.each do |byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each: |
+ upcased = String.new
+ buffer.each(:U8) do |offset, byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each_byte: |
+ upcased = String.new
+ buffer.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
diff --git a/benchmark/buffer_get.yml b/benchmark/buffer_get.yml
index bb9ca7e94a..9e1f99d64e 100644
--- a/benchmark/buffer_get.yml
+++ b/benchmark/buffer_get.yml
@@ -1,10 +1,25 @@
prelude: |
# frozen_string_literal: true
Warning[:experimental] = false
- buffer = IO::Buffer.new(32, IO::Buffer::MAPPED)
- string = "\0" * 32
+ string = "The quick brown fox jumped over the lazy dog."
+ buffer = IO::Buffer.for(string)
+ format = [:U32, :U32, :U32, :U32]
benchmark:
- buffer.get_value: |
- buffer.get_value(:U32, 0)
string.unpack1: |
- string.unpack1("N")
+ [
+ string.unpack1("N"),
+ string.unpack1("N", offset: 4),
+ string.unpack1("N", offset: 8),
+ string.unpack1("N", offset: 12),
+ ]
+ buffer.get_value: |
+ [
+ buffer.get_value(:U32, 0),
+ buffer.get_value(:U32, 4),
+ buffer.get_value(:U32, 8),
+ buffer.get_value(:U32, 12),
+ ]
+ buffer.get_values: |
+ buffer.get_values(format, 0)
+ string.unpack: |
+ string.unpack("NNNN")
diff --git a/benchmark/cgi_escape_html.yml b/benchmark/cgi_escape_html.yml
index af6abd08ac..655be9d7d8 100644
--- a/benchmark/cgi_escape_html.yml
+++ b/benchmark/cgi_escape_html.yml
@@ -1,32 +1,23 @@
-prelude: require 'cgi/escape'
+prelude: |
+ # frozen_string_literal: true
+ require 'cgi/escape'
benchmark:
- - name: escape_html_blank
- prelude: str = ""
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("")
loop_count: 20000000
- - name: escape_html_short_none
- prelude: str = "abcde"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("abcde")
loop_count: 20000000
- - name: escape_html_short_one
- prelude: str = "abcd<"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("abcd<")
loop_count: 20000000
- - name: escape_html_short_all
- prelude: str = "'&\"<>"
- script: CGI.escapeHTML(str)
+ - script: CGI.escapeHTML("'&\"<>")
loop_count: 5000000
- - name: escape_html_long_none
- prelude: str = "abcde" * 300
- script: CGI.escapeHTML(str)
+ - prelude: long_no_escape = "abcde" * 300
+ script: CGI.escapeHTML(long_no_escape)
loop_count: 1000000
- - name: escape_html_long_all
- prelude: str = "'&\"<>" * 10
- script: CGI.escapeHTML(str)
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: CGI.escapeHTML(long_all_escape)
loop_count: 1000000
- - name: escape_html_real
- prelude: | # http://example.com/
- str = <<~HTML
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
<body>
<div>
<h1>Example Domain</h1>
@@ -36,5 +27,5 @@ benchmark:
</div>
</body>
HTML
- script: CGI.escapeHTML(str)
+ script: CGI.escapeHTML(example_html)
loop_count: 1000000
diff --git a/benchmark/enum_minmax.yml b/benchmark/enum_minmax.yml
new file mode 100644
index 0000000000..9d01731abb
--- /dev/null
+++ b/benchmark/enum_minmax.yml
@@ -0,0 +1,25 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.min: set2.min
+ set10.min: set10.min
+ set100.min: set100.min
+ set1000.min: set1000.min
+ set10000.min: set10000.min
+ set2.max: set2.max
+ set10.max: set10.max
+ set100.max: set100.max
+ set1000.max: set1000.max
+ set10000.max: set10000.max
+ set2.minmax: set2.minmax
+ set10.minmax: set10.minmax
+ set100.minmax: set100.minmax
+ set1000.minmax: set1000.minmax
+ set10000.minmax: set10000.minmax
+
+loop_count: 10000
diff --git a/benchmark/enum_sort.yml b/benchmark/enum_sort.yml
new file mode 100644
index 0000000000..6f26e748c6
--- /dev/null
+++ b/benchmark/enum_sort.yml
@@ -0,0 +1,15 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.sort_by: set2.sort_by { 0 }
+ set10.sort_by: set10.sort_by { 0 }
+ set100.sort_by: set100.sort_by { 0 }
+ set1000.sort_by: set1000.sort_by { 0 }
+ set10000.sort_by: set10000.sort_by { 0 }
+
+loop_count: 10000
diff --git a/benchmark/erb_escape_html.yml b/benchmark/erb_escape_html.yml
new file mode 100644
index 0000000000..ca28d756e7
--- /dev/null
+++ b/benchmark/erb_escape_html.yml
@@ -0,0 +1,31 @@
+prelude: |
+ # frozen_string_literal: true
+ require 'erb'
+benchmark:
+ - script: ERB::Util.html_escape("")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcde")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcd<")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("'&\"<>")
+ loop_count: 5000000
+ - prelude: long_no_escape = "abcde" * 300
+ script: ERB::Util.html_escape(long_no_escape)
+ loop_count: 1000000
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: ERB::Util.html_escape(long_all_escape)
+ loop_count: 1000000
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
+ <body>
+ <div>
+ <h1>Example Domain</h1>
+ <p>This domain is established to be used for illustrative examples in documents. You may use this
+ domain in examples without prior coordination or asking for permission.</p>
+ <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+ </div>
+ </body>
+ HTML
+ script: ERB::Util.html_escape(example_html)
+ loop_count: 1000000
diff --git a/benchmark/lib/benchmark_driver/runner/mjit.rb b/benchmark/lib/benchmark_driver/runner/mjit.rb
index 1d4693e8be..3a58a620de 100644
--- a/benchmark/lib/benchmark_driver/runner/mjit.rb
+++ b/benchmark/lib/benchmark_driver/runner/mjit.rb
@@ -16,7 +16,7 @@ class BenchmarkDriver::Runner::Mjit < BenchmarkDriver::Runner::Ips
job.prelude = "#{job.prelude}\n#{<<~EOS}"
if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
__bmdv_ruby_i = 0
- while __bmdv_ruby_i < 10000 # jit_min_calls
+ while __bmdv_ruby_i < 10000 # MJIT call threshold
#{job.script}
__bmdv_ruby_i += 1
end
diff --git a/benchmark/numeric_methods.yml b/benchmark/numeric_methods.yml
index 433c2268a3..1384902935 100644
--- a/benchmark/numeric_methods.yml
+++ b/benchmark/numeric_methods.yml
@@ -10,4 +10,20 @@ benchmark:
int.finite?
infinite?: |
int.infinite?
+ integer_real: |
+ int.real
+ float_real: |
+ flo.real
+ integr_imag: |
+ int.imag
+ float_imag: |
+ flo.imag
+ integer_conj: |
+ int.conj
+ float_conj: |
+ flo.conj
+ integer_numerator: |
+ int.numerator
+ integer_denominator: |
+ int.denominator
loop_count: 20000000
diff --git a/benchmark/range_min.yml b/benchmark/range_min.yml
new file mode 100644
index 0000000000..9e60dd7308
--- /dev/null
+++ b/benchmark/range_min.yml
@@ -0,0 +1,2 @@
+benchmark:
+ - (1..10).min
diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml
index b8a69ed909..e65c00cca9 100644
--- a/benchmark/string_concat.yml
+++ b/benchmark/string_concat.yml
@@ -33,3 +33,13 @@ benchmark:
buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ interpolation: |
+ buffer = "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}"
diff --git a/benchmark/time_parse.yml b/benchmark/time_parse.yml
index a6d6948b9c..6060b58bc6 100644
--- a/benchmark/time_parse.yml
+++ b/benchmark/time_parse.yml
@@ -6,3 +6,5 @@ benchmark:
- Time.iso8601(iso8601)
- Time.parse(iso8601)
- Time.parse(inspect)
+ - Time.new(iso8601) rescue Time.iso8601(iso8601)
+ - Time.new(inspect) rescue Time.parse(inspect)
diff --git a/benchmark/vm_const.yml b/benchmark/vm_const.yml
index 6064d4eed0..8939ca0cd3 100644
--- a/benchmark/vm_const.yml
+++ b/benchmark/vm_const.yml
@@ -1,7 +1,13 @@
prelude: |
Const = 1
+ A = B = C = D = E = F = G = H = I = J = K = L = M = N = O = P = Q = R = S = T = U = V = W = X = Y = Z = 1
+ def foo
+ A; B; C; D; E; F; G; H; I; J; K; L; M; N; O; P; Q; R; S; T; U; V; W; X; Y; Z
+ end
benchmark:
vm_const: |
j = Const
k = Const
+ vm_const_many: |
+ foo
loop_count: 30000000
diff --git a/benchmark/vm_freezeobj.yml b/benchmark/vm_freezeobj.yml
new file mode 100644
index 0000000000..69a795a354
--- /dev/null
+++ b/benchmark/vm_freezeobj.yml
@@ -0,0 +1,6 @@
+prelude: |
+ objs = 100000.times.map { Object.new }
+benchmark:
+ vm_freeze_obj: |
+ objs.map(&:freeze)
+loop_count: 600
diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml
new file mode 100644
index 0000000000..9174af6965
--- /dev/null
+++ b/benchmark/vm_ivar_get.yml
@@ -0,0 +1,37 @@
+prelude: |
+ class Example
+ def initialize
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+ @levar = 1
+ end
+
+ def get_value_loop
+ sum = 0
+
+ i = 0
+ while i < 1000000
+ # 10 times to de-emphasize loop overhead
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ i += 1
+ end
+
+ return sum
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get: |
+ obj.get_value_loop
+loop_count: 100
diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml
new file mode 100644
index 0000000000..a1ccfb06ce
--- /dev/null
+++ b/benchmark/vm_ivar_get_unintialized.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def read
+ @uninitialized
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get_uninitialized: |
+ obj.read
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml
new file mode 100644
index 0000000000..7372ffcfbc
--- /dev/null
+++ b/benchmark/vm_ivar_lazy_set.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def lazy_set
+ @uninitialized ||= 123
+ end
+ end
+
+ objs = 10000000.times.map { Example.new }
+benchmark:
+ vm_ivar_lazy_set: |
+ objs.each(&:lazy_set)
+loop_count: 1
diff --git a/benchmark/vm_lvar_cond_set.yml b/benchmark/vm_lvar_cond_set.yml
new file mode 100644
index 0000000000..1845f9d12e
--- /dev/null
+++ b/benchmark/vm_lvar_cond_set.yml
@@ -0,0 +1,8 @@
+benchmark:
+ vm_lvar_cond_set: |
+ a ||= 1
+ b ||= 1
+ c ||= 1
+ d ||= 1
+ nil
+loop_count: 30000000
diff --git a/bignum.c b/bignum.c
index 9901a807b1..cb2c3b6f07 100644
--- a/bignum.c
+++ b/bignum.c
@@ -977,7 +977,7 @@ integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails,
{
/* nlp_bits stands for number of leading padding bits */
size_t num_bits = (wordsize * CHAR_BIT - nails) * numwords;
- size_t num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG;
+ size_t num_bdigits = roomof(num_bits, BITSPERDIG);
*nlp_bits_ret = (int)(num_bdigits * BITSPERDIG - num_bits);
return num_bdigits;
}
@@ -987,7 +987,7 @@ integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nail
{
/* BITSPERDIG = SIZEOF_BDIGIT * CHAR_BIT */
/* num_bits = (wordsize * CHAR_BIT - nails) * numwords */
- /* num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG */
+ /* num_bdigits = roomof(num_bits, BITSPERDIG) */
/* num_bits = CHAR_BIT * (wordsize * numwords) - nails * numwords = CHAR_BIT * num_bytes1 - nails * numwords */
size_t num_bytes1 = wordsize * numwords;
@@ -4184,7 +4184,6 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
}
if (!c || ISSPACE(c)) --str;
if (end) len = end - str;
- ASSERT_LEN();
}
c = *str;
c = conv_digit(c);
@@ -4587,11 +4586,14 @@ big_shift3(VALUE x, int lshift_p, size_t shift_numdigits, int shift_numbits)
if (lshift_p) {
if (LONG_MAX < shift_numdigits) {
- rb_raise(rb_eArgError, "too big number");
+ too_big:
+ rb_raise(rb_eRangeError, "shift width too big");
}
s1 = shift_numdigits;
s2 = shift_numbits;
+ if ((size_t)s1 != shift_numdigits) goto too_big;
xn = BIGNUM_LEN(x);
+ if (LONG_MAX/SIZEOF_BDIGIT <= xn+s1) goto too_big;
z = bignew(xn+s1+1, BIGNUM_SIGN(x));
zds = BDIGITS(z);
BDIGITS_ZERO(zds, s1);
diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb
index 3d42390254..f9b3e919b8 100755
--- a/bootstraptest/runner.rb
+++ b/bootstraptest/runner.rb
@@ -61,7 +61,7 @@ if !Dir.respond_to?(:mktmpdir)
end
# Configuration
-BT = Struct.new(:ruby,
+bt = Struct.new(:ruby,
:verbose,
:color,
:tty,
@@ -73,9 +73,78 @@ BT = Struct.new(:ruby,
:failed,
:reset,
:columns,
+ :window_width,
:width,
+ :indent,
:platform,
- ).new
+ )
+BT = Class.new(bt) do
+ def indent=(n)
+ super
+ if (self.columns ||= 0) < n
+ $stderr.print(' ' * (n - self.columns))
+ end
+ self.columns = indent
+ end
+
+ def putc(c)
+ unless self.quiet
+ if self.window_width == nil
+ unless w = ENV["COLUMNS"] and (w = w.to_i) > 0
+ w = 80
+ end
+ w -= 1
+ self.window_width = w
+ end
+ if self.window_width and self.columns >= self.window_width
+ $stderr.print "\n", " " * (self.indent ||= 0)
+ self.columns = indent
+ end
+ $stderr.print c
+ $stderr.flush
+ self.columns += 1
+ end
+ end
+
+ def wn=(wn)
+ unless wn == 1
+ if /(?:\A|\s)--jobserver-(?:auth|fds)=(?:(\d+),(\d+)|fifo:((?:\\.|\S)+))/ =~ ENV.delete("MAKEFLAGS")
+ begin
+ if fifo = $3
+ fifo.gsub!(/\\(?=.)/, '')
+ r = File.open(fifo, IO::RDONLY|IO::NONBLOCK|IO::BINARY)
+ w = File.open(fifo, IO::WRONLY|IO::NONBLOCK|IO::BINARY)
+ else
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ end
+ rescue => e
+ r.close if r
+ else
+ r.close_on_exec = true
+ w.close_on_exec = true
+ tokens = r.read_nonblock(wn > 0 ? wn : 1024, exception: false)
+ r.close
+ if String === tokens
+ tokens.freeze
+ auth = w
+ w = nil
+ at_exit {auth << tokens; auth.close}
+ wn = tokens.size + 1
+ else
+ w.close
+ wn = 1
+ end
+ end
+ end
+ if wn <= 0
+ require 'etc'
+ wn = [Etc.nprocessors / 2, 1].max
+ end
+ end
+ super wn
+ end
+end.new
BT_STATE = Struct.new(:count, :error).new
@@ -87,7 +156,7 @@ def main
BT.color = nil
BT.tty = nil
BT.quiet = false
- BT.wn = 1
+ # BT.wn = 1
dir = nil
quiet = false
tests = nil
@@ -122,12 +191,7 @@ def main
BT.quiet = true
true
when /\A-j(\d+)?/
- wn = $1.to_i
- if wn <= 0
- require 'etc'
- wn = [Etc.nprocessors / 2, 1].max
- end
- BT.wn = wn
+ BT.wn = $1.to_i
true
when /\A(-v|--v(erbose))\z/
BT.verbose = true
@@ -154,8 +218,7 @@ End
end
}
if tests and not ARGV.empty?
- $stderr.puts "--tests and arguments are exclusive"
- exit false
+ abort "--sets and arguments are exclusive"
end
tests ||= ARGV
tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty?
@@ -164,6 +227,7 @@ End
BT.progress = %w[- \\ | /]
BT.progress_bs = "\b" * BT.progress[0].size
BT.tty = $stderr.tty? if BT.tty.nil?
+ BT.wn ||= /-j(\d+)?/ =~ (ENV["MAKEFLAGS"] || ENV["MFLAGS"]) ? $1.to_i : 1
case BT.color
when nil
@@ -241,7 +305,7 @@ def concurrent_exec_test
end
end
- $stderr.print ' ' unless BT.quiet
+ BT.indent = 1
aq.close
i = 1
term_wn = 0
@@ -253,7 +317,7 @@ def concurrent_exec_test
when BT.tty
$stderr.print "#{BT.progress_bs}#{BT.progress[(i+=1) % BT.progress.size]}"
else
- $stderr.print '.'
+ BT.putc '.'
end
else
term_wn += 1
@@ -275,7 +339,7 @@ def exec_test(pathes)
# execute tests
if BT.wn > 1
- concurrent_exec_test if BT.wn > 1
+ concurrent_exec_test
else
prev_basename = nil
Assertion.all.each do |basename, assertions|
@@ -428,7 +492,7 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
elsif BT.verbose
$stderr.printf(". %.3f\n", t)
else
- $stderr.print '.'
+ BT.putc '.'
end
else
$stderr.print "#{BT.failed}F"
diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb
index 721a847145..3cb9d3eb39 100644
--- a/bootstraptest/test_attr.rb
+++ b/bootstraptest/test_attr.rb
@@ -34,3 +34,19 @@ assert_equal %{ok}, %{
print "ok"
end
}, '[ruby-core:15120]'
+
+assert_equal %{ok}, %{
+ class Big
+ attr_reader :foo
+ def initialize
+ @foo = "ok"
+ end
+ end
+
+ obj = Big.new
+ 100.times do |i|
+ obj.instance_variable_set(:"@ivar_\#{i}", i)
+ end
+
+ Big.new.foo
+}
diff --git a/bootstraptest/test_io.rb b/bootstraptest/test_io.rb
index ff26497696..666e5a011b 100644
--- a/bootstraptest/test_io.rb
+++ b/bootstraptest/test_io.rb
@@ -31,6 +31,7 @@ assert_finish 10, %q{
end
}, '[ruby-dev:32566]'
+/freebsd/ =~ RUBY_PLATFORM or
assert_finish 5, %q{
r, w = IO.pipe
Thread.new {
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index b29db7ab0e..67e66b03ee 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -283,8 +283,9 @@ assert_equal 30.times.map { 'ok' }.to_s, %q{
30.times.map{|i|
test i
}
-} unless ENV['RUN_OPTS'] =~ /--jit-min-calls=5/ || # This always fails with --jit-wait --jit-min-calls=5
- (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') # https://bugs.ruby-lang.org/issues/17878
+} unless ENV['RUN_OPTS'] =~ /--mjit-call-threshold=5/ || # This always fails with --mjit-wait --mjit-call-threshold=5
+ (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') || # https://bugs.ruby-lang.org/issues/17878
+ true # too flaky everywhere http://ci.rvm.jp/results/trunk@ruby-sp1/4321096
# Exception for empty select
assert_match /specify at least one ractor/, %q{
@@ -501,7 +502,7 @@ assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{
rs.delete r
n
}.sort
-}
+} unless /mswin/ =~ RUBY_PLATFORM # randomly hangs on mswin https://github.com/ruby/ruby/actions/runs/3753871445/jobs/6377551069#step:20:131
# Ractor.select also support multiple take, receive and yield
assert_equal '[true, true, true]', %q{
@@ -1472,7 +1473,7 @@ assert_equal "#{N}#{N}", %Q{
}
# enc_table
-assert_equal "#{N/10}", %Q{
+assert_equal "100", %Q{
Ractor.new do
loop do
Encoding.find("test-enc-#{rand(5_000)}").inspect
@@ -1481,7 +1482,7 @@ assert_equal "#{N/10}", %Q{
end
src = Encoding.find("UTF-8")
- #{N/10}.times{|i|
+ 100.times{|i|
src.replicate("test-enc-\#{i}")
}
}
@@ -1579,4 +1580,49 @@ assert_equal "ok", %q{
end
}
+assert_equal "ok", %q{
+ module M
+ def foo
+ @foo
+ end
+ end
+
+ class A
+ include M
+
+ def initialize
+ 100.times { |i| instance_variable_set(:"@var_#{i}", "bad: #{i}") }
+ @foo = 2
+ end
+ end
+
+ class B
+ include M
+
+ def initialize
+ @foo = 1
+ end
+ end
+
+ Ractor.new do
+ b = B.new
+ 100_000.times do
+ raise unless b.foo == 1
+ end
+ end
+
+ a = A.new
+ 100_000.times do
+ raise unless a.foo == 2
+ end
+
+ "ok"
+}
+
+assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{
+ Warning[:experimental] = $VERBOSE = true
+ STDERR.reopen(STDOUT)
+ eval("Ractor.new{}.take", nil, "test_ractor.rb", 1)
+}
+
end # if !ENV['GITHUB_WORKFLOW']
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 966a5f3002..5c655b8f25 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,33 @@
+# Regression test for yielding with autosplat to block with
+# optional parameters. https://github.com/Shopify/yjit/issues/313
+assert_equal '[:a, :b, :a, :b]', %q{
+ def yielder(arg) = yield(arg) + yield(arg)
+
+ yielder([:a, :b]) do |c = :c, d = :d|
+ [c, d]
+ end
+}
+
+# Regression test for GC mishap while doing shape transition
+assert_equal '[:ok]', %q{
+ # [Bug #19601]
+ class RegressionTest
+ def initialize
+ @a = @b = @fourth_ivar_does_shape_transition = nil
+ end
+
+ def extender
+ @first_extended_ivar = [:ok]
+ end
+ end
+
+ GC.stress = true
+
+ # Used to crash due to GC run in rb_ensure_iv_list_size()
+ # not marking the newly allocated [:ok].
+ RegressionTest.new.extender.itself
+}
+
assert_equal 'true', %q{
# regression test for tracking type of locals for too long
def local_setting_cmp(five)
@@ -45,6 +75,29 @@ assert_normal_exit %q{
Object.send(:remove_const, :Foo)
}
+assert_normal_exit %q{
+ # Test to ensure send on overriden c functions
+ # doesn't corrupt the stack
+ class Bar
+ def bar(x)
+ x
+ end
+ end
+
+ class Foo
+ def bar
+ Bar.new
+ end
+ end
+
+ foo = Foo.new
+ # before this change, this line would error
+ # because "s" would still be on the stack
+ # String.to_s is the overridden method here
+ p foo.bar.bar("s".__send__(:to_s))
+}
+
+
assert_equal '[nil, nil, nil, nil, nil, nil]', %q{
[NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
klass.class_eval("def foo = @foo")
@@ -198,6 +251,8 @@ assert_equal 'string', %q{
# Check that exceptions work when getting global variable
assert_equal 'rescued', %q{
+ Warning[:deprecated] = true
+
module Warning
def warn(message)
raise
@@ -1118,6 +1173,38 @@ assert_equal '42', %q{
run
}
+# splatting an empty array on a specialized method
+assert_equal 'ok', %q{
+ def run
+ "ok".to_s(*[])
+ end
+
+ run
+ run
+}
+
+# splatting an single element array on a specialized method
+assert_equal '[1]', %q{
+ def run
+ [].<<(*[1])
+ end
+
+ run
+ run
+}
+
+# specialized method with wrong args
+assert_equal 'ok', %q{
+ def run(x)
+ "bad".to_s(123) if x
+ rescue
+ :ok
+ end
+
+ run(false)
+ run(true)
+}
+
# getinstancevariable on Symbol
assert_equal '[nil, nil]', %q{
# @foo to exercise the getinstancevariable instruction
@@ -2150,7 +2237,7 @@ assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
events.compiled(events)
events
-}
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
# test enabling a TracePoint that targets a particular line in a C method call
assert_equal '[true]', %q{
@@ -2232,7 +2319,7 @@ assert_equal '[[:c_call, :itself]]', %q{
tp.enable { shouldnt_compile }
events
-}
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
# test enabling c_return tracing before compiling
assert_equal '[[:c_return, :itself, main]]', %q{
@@ -2247,6 +2334,26 @@ assert_equal '[[:c_return, :itself, main]]', %q{
tp.enable { shouldnt_compile }
events
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
+
+# test c_call invalidation
+assert_equal '[[:c_call, :itself]]', %q{
+ # enable the event once to make sure invalidation
+ # happens the second time we enable it
+ TracePoint.new(:c_call) {}.enable{}
+
+ def compiled
+ itself
+ end
+
+ # assume first call compiles
+ compiled
+
+ events = []
+ tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
+ tp.enable { compiled }
+
+ events
}
# test enabling tracing for a suspended fiber
@@ -2901,11 +3008,20 @@ assert_equal 'new', %q{
foo
end
+ def bar
+ :bar
+ end
+
+
test
test
RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+ # Old simulat_omm! leaves one byte of space and this fills it up
+ bar
+ bar
+
def foo
:new
end
@@ -3066,3 +3182,349 @@ assert_equal '10', %q{
a.length
}
+
+# checktype
+assert_equal 'false', %q{
+ def function()
+ [1, 2] in [Integer, String]
+ end
+ function()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_ATTRSET)
+assert_equal 'foo', %q{
+ class Foo
+ attr_writer :foo
+
+ def foo()
+ self.foo = "foo"
+ end
+ end
+ foo = Foo.new
+ foo.foo
+}
+
+# anytostring, intern
+assert_equal 'true', %q{
+ def foo()
+ :"#{true}"
+ end
+ foo()
+}
+
+# toregexp, objtostring
+assert_equal '/true/', %q{
+ def foo()
+ /#{true}/
+ end
+ foo().inspect
+}
+
+# concatstrings, objtostring
+assert_equal '9001', %q{
+ def foo()
+ "#{9001}"
+ end
+ foo()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_CFUNC)
+assert_equal 'nil', %q{
+ def foo
+ nil.inspect # argc: 0
+ end
+ foo
+}
+assert_equal '4', %q{
+ def foo
+ 2.pow(2) # argc: 1
+ end
+ foo
+}
+assert_equal 'aba', %q{
+ def foo
+ "abc".tr("c", "a") # argc: 2
+ end
+ foo
+}
+assert_equal 'true', %q{
+ def foo
+ respond_to?(:inspect) # argc: -1
+ end
+ foo
+}
+assert_equal '["a", "b"]', %q{
+ def foo
+ "a\nb".lines(chomp: true) # kwargs
+ end
+ foo
+}
+
+# invokebuiltin
+assert_equal '123', %q{
+ def foo(obj)
+ obj.foo = 123
+ end
+
+ struct = Struct.new(:foo)
+ obj = struct.new
+ foo(obj)
+}
+
+# invokebuiltin_delegate
+assert_equal '.', %q{
+ def foo(path)
+ Dir.open(path).path
+ end
+ foo(".")
+}
+
+# opt_invokebuiltin_delegate_leave
+assert_equal '[0]', %q{"\x00".unpack("c")}
+
+# opt_send_without_block (VM_METHOD_TYPE_ISEQ)
+assert_equal '1', %q{
+ def foo = 1
+ def bar = foo
+ bar
+}
+assert_equal '[1, 2, 3]', %q{
+ def foo(a, b) = [1, a, b]
+ def bar = foo(2, 3)
+ bar
+}
+assert_equal '[1, 2, 3, 4, 5, 6]', %q{
+ def foo(a, b, c:, d:, e: 0, f: 6) = [a, b, c, d, e, f]
+ def bar = foo(1, 2, c: 3, d: 4, e: 5)
+ bar
+}
+assert_equal '[1, 2, 3, 4]', %q{
+ def foo(a, b = 2) = [a, b]
+ def bar = foo(1) + foo(3, 4)
+ bar
+}
+
+assert_equal '1', %q{
+ def foo(a) = a
+ def bar = foo(1) { 2 }
+ bar
+}
+assert_equal '[1, 2]', %q{
+ def foo(a, &block) = [a, block.call]
+ def bar = foo(1) { 2 }
+ bar
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_IVAR)
+assert_equal 'foo', %q{
+ class Foo
+ attr_reader :foo
+
+ def initialize
+ @foo = "foo"
+ end
+ end
+ Foo.new.foo
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_OPTIMIZED)
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("bar").bar = "foo"
+}
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("foo").bar
+}
+
+# getblockparamproxy
+assert_equal 'foo', %q{
+ def foo(&block)
+ block.call
+ end
+ foo { "foo" }
+}
+
+# getblockparam
+assert_equal 'foo', %q{
+ def foo(&block)
+ block
+ end
+ foo { "foo" }.call
+}
+
+assert_equal '[1, 2]', %q{
+ def foo
+ x = [2]
+ [1, *x]
+ end
+
+ foo
+ foo
+}
+
+# respond_to? with changing symbol
+assert_equal 'false', %q{
+ def foo(name)
+ :sym.respond_to?(name)
+ end
+ foo(:to_s)
+ foo(:to_s)
+ foo(:not_exist)
+}
+
+# respond_to? with method being defined
+assert_equal 'true', %q{
+ def foo
+ :sym.respond_to?(:not_yet_defined)
+ end
+ foo
+ foo
+ module Kernel
+ def not_yet_defined = true
+ end
+ foo
+}
+
+# respond_to? with undef method
+assert_equal 'false', %q{
+ module Kernel
+ def to_be_removed = true
+ end
+ def foo
+ :sym.respond_to?(:to_be_removed)
+ end
+ foo
+ foo
+ class Object
+ undef_method :to_be_removed
+ end
+ foo
+}
+
+# respond_to? with respond_to_missing?
+assert_equal 'true', %q{
+ class Foo
+ end
+ def foo(x)
+ x.respond_to?(:bar)
+ end
+ foo(Foo.new)
+ foo(Foo.new)
+ class Foo
+ def respond_to_missing?(*) = true
+ end
+ foo(Foo.new)
+}
+
+# bmethod
+assert_equal '[1, 2, 3]', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ 3.times.map { |i| foo + i }
+}
+
+# return inside bmethod
+assert_equal 'ok', %q{
+ define_method(:foo) do
+ 1.tap { return :ok }
+ end
+
+ foo
+}
+
+# bmethod optional and keywords
+assert_equal '[[1, nil, 2]]', %q{
+ define_method(:opt_and_kwargs) do |a = {}, b: nil, c: nil|
+ [a, b, c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, c: 2) }.uniq
+}
+
+# bmethod with forwarded block
+assert_equal '2', %q{
+ define_method(:foo) do |&block|
+ block.call
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { 1 }
+ bar { 2 }
+}
+
+# bmethod with forwarded block and arguments
+assert_equal '5', %q{
+ define_method(:foo) do |n, &block|
+ n + block.call
+ end
+
+ def bar(n, &block)
+ foo(n, &block)
+ end
+
+ bar(0) { 1 }
+ bar(3) { 2 }
+}
+
+# bmethod with forwarded unwanted block
+assert_equal '1', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { }
+ bar { }
+}
+
+# test for return stub lifetime issue
+assert_equal '1', %q{
+ def foo(n)
+ if n == 2
+ return 1.times { Object.define_method(:foo) {} }
+ end
+
+ foo(n + 1)
+ end
+
+ foo(1)
+}
+
+# case-when with redefined ===
+assert_equal 'ok', %q{
+ class Symbol
+ def ===(a)
+ true
+ end
+ end
+
+ def cw(arg)
+ case arg
+ when :b
+ :ok
+ when 4
+ :ng
+ end
+ end
+
+ cw(4)
+}
+
+assert_normal_exit %{
+ class Bug20997
+ def foo(&) = self.class.name(&)
+
+ new.foo
+ end
+}
diff --git a/builtin.h b/builtin.h
index b827b28928..38ad5a1629 100644
--- a/builtin.h
+++ b/builtin.h
@@ -13,11 +13,11 @@ struct rb_builtin_function {
const char * const name;
// for jit
- void (*compiler)(FILE *, long, unsigned, bool);
+ void (*compiler)(VALUE, long, unsigned, bool);
};
#define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity, _compiler) {\
- .name = #_name, \
+ .name = _i < 0 ? NULL : #_name, \
.func_ptr = (void *)_fname, \
.argc = _arity, \
.index = _i, \
diff --git a/ccan/check_type/check_type.h b/ccan/check_type/check_type.h
index e795ad71d0..659e1a5a83 100644
--- a/ccan/check_type/check_type.h
+++ b/ccan/check_type/check_type.h
@@ -44,7 +44,7 @@
* ((encl_type *) \
* ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_check_type(expr, type) \
((typeof(expr) *)0 != (type *)0)
diff --git a/ccan/container_of/container_of.h b/ccan/container_of/container_of.h
index b30c347d57..872bb6ea6e 100644
--- a/ccan/container_of/container_of.h
+++ b/ccan/container_of/container_of.h
@@ -112,7 +112,7 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
* return i;
* }
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_container_of_var(member_ptr, container_var, member) \
ccan_container_of(member_ptr, typeof(*container_var), member)
#else
@@ -131,7 +131,7 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
* structure memory layout.
*
*/
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_container_off_var(var, member) \
ccan_container_off(typeof(*var), member)
#else
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 91787bfdb3..30b2af04e9 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -770,7 +770,7 @@ static inline struct ccan_list_node *ccan_list_node_from_off_(void *ptr, size_t
(ccan_container_off_var(var, member) + \
ccan_check_type(var->member, struct ccan_list_node))
-#if HAVE_TYPEOF
+#if defined(HAVE_TYPEOF) && HAVE_TYPEOF
#define ccan_list_typeof(var) typeof(var)
#else
#define ccan_list_typeof(var) void *
diff --git a/class.c b/class.c
index 54d9e6e177..cf0b7b821f 100644
--- a/class.c
+++ b/class.c
@@ -64,7 +64,7 @@ push_subclass_entry_to_list(VALUE super, VALUE klass)
void
rb_class_subclass_add(VALUE super, VALUE klass)
{
- if (super && super != Qundef) {
+ if (super && !UNDEF_P(super)) {
rb_subclass_entry_t *entry = push_subclass_entry_to_list(super, klass);
RCLASS_SUBCLASS_ENTRY(klass) = entry;
}
@@ -197,7 +197,7 @@ class_alloc(VALUE flags, VALUE klass)
{
size_t alloc_size = sizeof(struct RClass);
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
alloc_size += sizeof(rb_classext_t);
#endif
@@ -206,17 +206,13 @@ class_alloc(VALUE flags, VALUE klass)
if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
RVARGC_NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size);
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
memset(RCLASS_EXT(obj), 0, sizeof(rb_classext_t));
-# if SIZEOF_SERIAL_T != SIZEOF_VALUE
- RCLASS(obj)->class_serial_ptr = ZALLOC(rb_serial_t);
-# endif
#else
obj->ptr = ZALLOC(rb_classext_t);
#endif
/* ZALLOC
- RCLASS_IV_TBL(obj) = 0;
RCLASS_CONST_TBL(obj) = 0;
RCLASS_M_TBL(obj) = 0;
RCLASS_IV_INDEX_TBL(obj) = 0;
@@ -226,7 +222,6 @@ class_alloc(VALUE flags, VALUE klass)
RCLASS_MODULE_SUBCLASSES(obj) = NULL;
*/
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
- RCLASS_SERIAL(obj) = rb_next_class_serial();
RB_OBJ_WRITE(obj, &RCLASS_REFINED_CLASS(obj), Qnil);
RCLASS_ALLOCATOR(obj) = 0;
@@ -282,7 +277,7 @@ rb_class_update_superclasses(VALUE klass)
VALUE super = RCLASS_SUPER(klass);
if (!RB_TYPE_P(klass, T_CLASS)) return;
- if (super == Qundef) return;
+ if (UNDEF_P(super)) return;
// If the superclass array is already built
if (RCLASS_SUPERCLASSES(klass))
@@ -331,7 +326,13 @@ rb_class_new(VALUE super)
{
Check_Type(super, T_CLASS);
rb_check_inheritable(super);
- return rb_class_boot(super);
+ VALUE klass = rb_class_boot(super);
+
+ if (super != rb_cObject && super != rb_cBasicObject) {
+ RCLASS_EXT(klass)->max_iv_count = RCLASS_EXT(super)->max_iv_count;
+ }
+
+ return klass;
}
VALUE
@@ -403,28 +404,57 @@ class_init_copy_check(VALUE clone, VALUE orig)
}
}
+struct cvc_table_copy_ctx {
+ VALUE clone;
+ struct rb_id_table * new_table;
+};
+
+static enum rb_id_table_iterator_result
+cvc_table_copy(ID id, VALUE val, void *data) {
+ struct cvc_table_copy_ctx *ctx = (struct cvc_table_copy_ctx *)data;
+ struct rb_cvar_class_tbl_entry * orig_entry;
+ orig_entry = (struct rb_cvar_class_tbl_entry *)val;
+
+ struct rb_cvar_class_tbl_entry *ent;
+
+ ent = ALLOC(struct rb_cvar_class_tbl_entry);
+ ent->class_value = ctx->clone;
+ ent->cref = orig_entry->cref;
+ ent->global_cvar_state = orig_entry->global_cvar_state;
+ rb_id_table_insert(ctx->new_table, id, (VALUE)ent);
+
+ RB_OBJ_WRITTEN(ctx->clone, Qundef, ent->cref);
+
+ return ID_TABLE_CONTINUE;
+}
+
static void
copy_tables(VALUE clone, VALUE orig)
{
- if (RCLASS_IV_TBL(clone)) {
- st_free_table(RCLASS_IV_TBL(clone));
- RCLASS_IV_TBL(clone) = 0;
- }
if (RCLASS_CONST_TBL(clone)) {
rb_free_const_table(RCLASS_CONST_TBL(clone));
RCLASS_CONST_TBL(clone) = 0;
}
+ if (RCLASS_CVC_TBL(orig)) {
+ struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(orig);
+ struct rb_id_table *rb_cvc_tbl_dup = rb_id_table_create(rb_id_table_size(rb_cvc_tbl));
+
+ struct cvc_table_copy_ctx ctx;
+ ctx.clone = clone;
+ ctx.new_table = rb_cvc_tbl_dup;
+ rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
+ RCLASS_CVC_TBL(clone) = rb_cvc_tbl_dup;
+ }
+ rb_id_table_free(RCLASS_M_TBL(clone));
RCLASS_M_TBL(clone) = 0;
- if (RCLASS_IV_TBL(orig)) {
+ if (!RB_TYPE_P(clone, T_ICLASS)) {
st_data_t id;
rb_iv_tbl_copy(clone, orig);
CONST_ID(id, "__tmp_classpath__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
+ rb_attr_delete(clone, id);
CONST_ID(id, "__classpath__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
- CONST_ID(id, "__classid__");
- st_delete(RCLASS_IV_TBL(clone), &id, 0);
+ rb_attr_delete(clone, id);
}
if (RCLASS_CONST_TBL(orig)) {
struct clone_const_arg arg;
@@ -526,7 +556,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
prev_clone_p = clone_p;
RCLASS_M_TBL(clone_p) = RCLASS_M_TBL(p);
RCLASS_CONST_TBL(clone_p) = RCLASS_CONST_TBL(p);
- RCLASS_IV_TBL(clone_p) = RCLASS_IV_TBL(p);
RCLASS_ALLOCATOR(clone_p) = RCLASS_ALLOCATOR(p);
if (RB_TYPE_P(clone, T_CLASS)) {
RCLASS_SET_INCLUDER(clone_p, clone);
@@ -613,16 +642,14 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass));
RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(klass);
- if (RCLASS_IV_TBL(klass)) {
- rb_iv_tbl_copy(clone, klass);
- }
+ rb_iv_tbl_copy(clone, klass);
if (RCLASS_CONST_TBL(klass)) {
struct clone_const_arg arg;
arg.tbl = RCLASS_CONST_TBL(clone) = rb_id_table_create(0);
arg.klass = clone;
rb_id_table_foreach(RCLASS_CONST_TBL(klass), clone_const_i, &arg);
}
- if (attach != Qundef) {
+ if (!UNDEF_P(attach)) {
rb_singleton_class_attached(clone, attach);
}
RCLASS_M_TBL_INIT(clone);
@@ -931,7 +958,7 @@ rb_define_class_under(VALUE outer, const char *name, VALUE super)
}
VALUE
-rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super)
{
VALUE klass;
@@ -948,8 +975,6 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super)
" (%"PRIsVALUE" is given but was %"PRIsVALUE")",
outer, rb_id2str(id), RCLASS_SUPER(klass), super);
}
- /* Class may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(klass);
return klass;
}
@@ -961,12 +986,19 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super)
rb_set_class_path_string(klass, outer, rb_id2str(id));
rb_const_set(outer, id, klass);
rb_class_inherited(super, klass);
- rb_vm_add_root_module(klass);
return klass;
}
VALUE
+rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+{
+ VALUE klass = rb_define_class_id_under_no_pin(outer, id, super);
+ rb_vm_add_root_module(klass);
+ return klass;
+}
+
+VALUE
rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
@@ -1068,13 +1100,10 @@ rb_include_class_new(VALUE module, VALUE super)
module = METACLASS_OF(module);
}
RUBY_ASSERT(!RB_TYPE_P(module, T_ICLASS));
- if (!RCLASS_IV_TBL(module)) {
- RCLASS_IV_TBL(module) = st_init_numtable();
- }
if (!RCLASS_CONST_TBL(module)) {
RCLASS_CONST_TBL(module) = rb_id_table_create(0);
}
- RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
+
RCLASS_CVC_TBL(klass) = RCLASS_CVC_TBL(module);
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
@@ -1116,8 +1145,8 @@ rb_include_module(VALUE klass, VALUE module)
iclass = iclass->next;
}
- int do_include = 1;
while (iclass) {
+ int do_include = 1;
VALUE check_class = iclass->klass;
/* During lazy sweeping, iclass->klass could be a dead object that
* has not yet been swept. */
@@ -1184,7 +1213,7 @@ static int
do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super, bool check_cyclic)
{
VALUE p, iclass, origin_stack = 0;
- int method_changed = 0, add_subclass;
+ int method_changed = 0;
long origin_len;
VALUE klass_origin = RCLASS_ORIGIN(klass);
VALUE original_klass = klass;
@@ -1248,7 +1277,6 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
iclass = rb_include_class_new(module, super_class);
c = RCLASS_SET_SUPER(c, iclass);
RCLASS_SET_INCLUDER(iclass, klass);
- add_subclass = TRUE;
if (module != RCLASS_ORIGIN(module)) {
if (!origin_stack) origin_stack = rb_ary_hidden_new(2);
VALUE origin[2] = {iclass, RCLASS_ORIGIN(module)};
@@ -1259,14 +1287,11 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
RCLASS_SET_ORIGIN(RARRAY_AREF(origin_stack, (origin_len -= 2)), iclass);
RICLASS_SET_ORIGIN_SHARED_MTBL(iclass);
rb_ary_resize(origin_stack, origin_len);
- add_subclass = FALSE;
}
- if (add_subclass) {
- VALUE m = module;
- if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
- rb_module_add_to_subclasses_list(m, iclass);
- }
+ VALUE m = module;
+ if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
+ rb_module_add_to_subclasses_list(m, iclass);
if (BUILTIN_TYPE(klass) == T_MODULE && FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
VALUE refined_class =
@@ -1583,6 +1608,27 @@ class_descendants(VALUE klass, bool immediate_only)
* A.subclasses #=> [D, B]
* B.subclasses #=> [C]
* C.subclasses #=> []
+ *
+ * Anonymous subclasses (not associated with a constant) are
+ * returned, too:
+ *
+ * c = Class.new(A)
+ * A.subclasses # => [#<Class:0x00007f003c77bd78>, D, B]
+ *
+ * Note that the parent does not hold references to subclasses
+ * and doesn't prevent them from being garbage collected. This
+ * means that the subclass might disappear when all references
+ * to it are dropped:
+ *
+ * # drop the reference to subclass, it can be garbage-collected now
+ * c = nil
+ *
+ * A.subclasses
+ * # It can be
+ * # => [#<Class:0x00007f003c77bd78>, D, B]
+ * # ...or just
+ * # => [D, B]
+ * # ...depending on whether garbage collector was run
*/
VALUE
@@ -1591,6 +1637,33 @@ rb_class_subclasses(VALUE klass)
return class_descendants(klass, true);
}
+/*
+ * call-seq:
+ * attached_object -> object
+ *
+ * Returns the object for which the receiver is the singleton class.
+ *
+ * Raises an TypeError if the class is not a singleton class.
+ *
+ * class Foo; end
+ *
+ * Foo.singleton_class.attached_object #=> Foo
+ * Foo.attached_object #=> TypeError: `Foo' is not a singleton class
+ * Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
+ * TrueClass.attached_object #=> TypeError: `TrueClass' is not a singleton class
+ * NilClass.attached_object #=> TypeError: `NilClass' is not a singleton class
+ */
+
+VALUE
+rb_class_attached_object(VALUE klass)
+{
+ if (!FL_TEST(klass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
+ }
+
+ return rb_attr_get(klass, id_attached);
+}
+
static void
ins_methods_push(st_data_t name, st_data_t ary)
{
@@ -1641,10 +1714,7 @@ ins_methods_pub_i(st_data_t name, st_data_t type, st_data_t ary)
static int
ins_methods_undef_i(st_data_t name, st_data_t type, st_data_t ary)
{
- if ((rb_method_visibility_t)type == METHOD_VISI_UNDEF) {
- ins_methods_push(name, ary);
- }
- return ST_CONTINUE;
+ return ins_methods_type_i(name, type, ary, METHOD_VISI_UNDEF);
}
struct method_entry_arg {
@@ -1755,6 +1825,15 @@ class_instance_method_list(int argc, const VALUE *argv, VALUE mod, int obj, int
* B.instance_methods(true).include?(:method1) #=> true
* C.instance_methods(false) #=> [:method3]
* C.instance_methods.include?(:method2) #=> true
+ *
+ * Note that method visibility changes in the current class, as well as aliases,
+ * are considered as methods of the current class by this method:
+ *
+ * class C < B
+ * alias method4 method2
+ * protected :method2
+ * end
+ * C.instance_methods(false).sort #=> [:method2, :method3, :method4]
*/
VALUE
@@ -2112,9 +2191,7 @@ singleton_class_of(VALUE obj)
klass = METACLASS_OF(obj);
if (!(FL_TEST(klass, FL_SINGLETON) &&
rb_attr_get(klass, id_attached) == obj)) {
- rb_serial_t serial = RCLASS_SERIAL(klass);
klass = rb_make_metaclass(obj, klass);
- RCLASS_SERIAL(klass) = serial;
}
RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
@@ -2128,7 +2205,7 @@ rb_freeze_singleton_class(VALUE x)
/* should not propagate to meta-meta-class, and so on */
if (!(RBASIC(x)->flags & FL_SINGLETON)) {
VALUE klass = RBASIC_CLASS(x);
- if (klass && (klass = RCLASS_ORIGIN(klass)) != 0 &&
+ if (klass && // no class when hidden from ObjectSpace
FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
OBJ_FREEZE_RAW(klass);
}
diff --git a/common.mk b/common.mk
index 1f33a9a3ac..126053c9c8 100644
--- a/common.mk
+++ b/common.mk
@@ -18,8 +18,10 @@ mflags = $(MFLAGS)
gnumake_recursive =
enable_shared = $(ENABLE_SHARED:no=)
-UNICODE_VERSION = 14.0.0
-UNICODE_EMOJI_VERSION = 14.0
+UNICODE_VERSION = 15.0.0
+UNICODE_EMOJI_VERSION_0 = $(UNICODE_VERSION)///
+UNICODE_EMOJI_VERSION_1 = $(UNICODE_EMOJI_VERSION_0:.0///=)
+UNICODE_EMOJI_VERSION = $(UNICODE_EMOJI_VERSION_1:///=)
UNICODE_BETA = NO
### set the following environment variable or uncomment the line if
@@ -41,14 +43,14 @@ RUN_OPTS = --disable-gems
# GITPULLOPTIONS = --no-tags
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR) $(incflags)
GEM_HOME =
GEM_PATH =
GEM_VENDOR =
BENCHMARK_DRIVER_GIT_URL = https://github.com/benchmark-driver/benchmark-driver
-BENCHMARK_DRIVER_GIT_REF = v0.15.18
+BENCHMARK_DRIVER_GIT_REF = v0.16.0
SIMPLECOV_GIT_URL = https://github.com/colszowka/simplecov.git
SIMPLECOV_GIT_REF = v0.17.0
SIMPLECOV_HTML_GIT_URL = https://github.com/colszowka/simplecov-html.git
@@ -82,7 +84,7 @@ EXTSOLIBS =
MINIOBJS = $(ARCHMINIOBJS) miniinit.$(OBJEXT) dmyext.$(OBJEXT)
ENC_MK = enc.mk
MAKE_ENC = -f $(ENC_MK) V="$(V)" UNICODE_HDR_DIR="$(UNICODE_HDR_DIR)" \
- RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" $(mflags)
+ RUBY="$(BOOTSTRAPRUBY)" MINIRUBY="$(BOOTSTRAPRUBY)" $(mflags)
COMMONOBJS = array.$(OBJEXT) \
ast.$(OBJEXT) \
@@ -113,7 +115,7 @@ COMMONOBJS = array.$(OBJEXT) \
math.$(OBJEXT) \
memory_view.$(OBJEXT) \
mjit.$(OBJEXT) \
- mjit_compiler.$(OBJEXT) \
+ mjit_c.$(OBJEXT) \
node.$(OBJEXT) \
numeric.$(OBJEXT) \
object.$(OBJEXT) \
@@ -134,6 +136,7 @@ COMMONOBJS = array.$(OBJEXT) \
regsyntax.$(OBJEXT) \
ruby.$(OBJEXT) \
scheduler.$(OBJEXT) \
+ shape.$(OBJEXT) \
signal.$(OBJEXT) \
sprintf.$(OBJEXT) \
st.$(OBJEXT) \
@@ -195,6 +198,7 @@ INSTRUBY_ARGS = $(SCRIPT_ARGS) \
INSTALL_PROG_MODE = 0755
INSTALL_DATA_MODE = 0644
+BOOTSTRAPRUBY_COMMAND = $(BOOTSTRAPRUBY) $(BOOTSTRAPRUBY_OPT)
TESTSDIR = $(srcdir)/test
TOOL_TESTSDIR = $(tooldir)/test
TEST_EXCLUDES = --excludes-dir=$(TESTSDIR)/excludes --name=!/memory_leak/
@@ -216,12 +220,27 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \
-e "noraise {ln(src, dest)} or" \
-e "cp(src, dest)"
+# For release builds
+YJIT_RUSTC_ARGS = --crate-name=yjit \
+ --crate-type=staticlib \
+ --edition=2021 \
+ -g \
+ -C opt-level=3 \
+ -C overflow-checks=on \
+ '--out-dir=$(CARGO_TARGET_DIR)/release/' \
+ $(top_srcdir)/yjit/src/lib.rs
all: $(SHOWFLAGS) main docs
main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs
@$(NULLCMD)
+main: $(srcdir)/lib/ruby_vm/mjit/instruction.rb
+srcs: $(srcdir)/lib/ruby_vm/mjit/instruction.rb
+$(srcdir)/lib/ruby_vm/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb $(srcdir)/insns.def
+ $(ECHO) generating $@
+ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@
+
mjit-headers: $(MJIT_SUPPORT)-mjit-headers
no-mjit-headers: PHONY
yes-mjit-headers: mjit_config.h PHONY
@@ -229,6 +248,9 @@ yes-mjit-headers: mjit_config.h PHONY
mjit.$(OBJEXT): mjit_config.h
mjit_config.h: Makefile
+.PHONY: mjit-bindgen
+mjit-bindgen:
+ $(Q) $(BASERUBY) -rrubygems -C $(srcdir)/tool/mjit bindgen.rb $(CURDIR)
# These rules using MJIT_HEADER_SUFFIX must be in common.mk, not
# Makefile.in, in order to override the macro in defs/universal.mk.
@@ -270,6 +292,8 @@ showflags:
" LC_ALL = $(LC_ALL)" \
" LC_CTYPE = $(LC_CTYPE)" \
" MFLAGS = $(MFLAGS)" \
+ " RUSTC = $(RUSTC)" \
+ " YJIT_RUSTC_ARGS = $(YJIT_RUSTC_ARGS)" \
$(MESSAGE_END)
-@$(CC_VERSION)
@@ -610,12 +634,13 @@ clear-installed-list: PHONY
clean: clean-ext clean-enc clean-golf clean-docs clean-extout clean-local clean-platform clean-spec
clean-local:: clean-runnable
- $(Q)$(RM) $(OBJS) $(MINIOBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
+ $(Q)$(RM) $(OBJS) $(MINIOBJS) $(INITOBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
$(Q)$(RM) $(PROGRAM) $(WPROGRAM) miniruby$(EXEEXT) dmyext.$(OBJEXT) dmyenc.$(OBJEXT) $(ARCHFILE) .*.time
$(Q)$(RM) y.tab.c y.output encdb.h transdb.h config.log rbconfig.rb $(ruby_pc) $(COROUTINE_H:/Context.h=/.time)
$(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT)
$(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D) builtin_binary.inc
- -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine 2> $(NULL) || $(NULLCMD)
+ -$(Q)$(RMALL) yjit/target
+ -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit 2> $(NULL) || $(NULLCMD)
bin/clean-runnable:: PHONY
$(Q)$(CHDIR) bin 2>$(NULL) && $(RM) $(PROGRAM) $(WPROGRAM) $(GORUBY)$(EXEEXT) bin/*.$(DLEXT) 2>$(NULL) || $(NULLCMD)
@@ -741,7 +766,7 @@ clean-spec: PHONY
-$(Q) $(RMDIRS) $(RUBYSPEC_CAPIEXT) 2> $(NULL) || $(NULLCMD)
-$(Q) $(RMALL) rubyspec_temp
-check: main test test-tool test-all test-spec
+check: main $(DOT_WAIT) test $(DOT_WAIT) test-tool $(DOT_WAIT) test-all
$(ECHO) check succeeded
-$(Q) : : "run only on sh"; \
if [ x"$(GIT)" != x ] && $(CHDIR) "$(srcdir)" && \
@@ -755,26 +780,34 @@ fake: $(CROSS_COMPILING)-fake
yes-fake: $(arch)-fake.rb $(RBCONFIG) PHONY
no-fake -fake: PHONY
-# really doesn't depend on .o, just ensure newer than headers which
-# version.o depends on.
-$(arch)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_erb.rb version.$(OBJEXT) miniruby$(EXEEXT)
+$(HAVE_BASERUBY:no=)$(arch)-fake.rb: miniruby$(EXEEXT)
+
+# actually depending on other headers more.
+$(arch:noarch=ignore)-fake.rb: $(top_srcdir)/revision.h $(top_srcdir)/version.h $(srcdir)/version.c
+$(arch:noarch=ignore)-fake.rb: {$(VPATH)}id.h {$(VPATH)}vm_opts.h
+
+$(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_erb.rb
$(ECHO) generating $@
$(Q) $(CPP) -DRUBY_EXPORT $(INCFLAGS) $(CPPFLAGS) "$(srcdir)/version.c" | \
$(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \
- i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)"
+ i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" \
+ LIBPATHENV="$(LIBPATHENV)" PRELOADENV="$(PRELOADENV)" LIBRUBY_SO="$(LIBRUBY_SO)"
+
+noarch-fake.rb: # prerequisite of yes-fake
+ $(Q) exit > $@
btest: $(TEST_RUNNABLE)-btest
no-btest: PHONY
yes-btest: yes-fake miniruby$(EXEEXT) PHONY
$(ACTIONS_GROUP)
- $(Q)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
+ $(Q)$(gnumake_recursive)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
$(ACTIONS_ENDGROUP)
btest-ruby: $(TEST_RUNNABLE)-btest-ruby
no-btest-ruby: PHONY
yes-btest-ruby: prog PHONY
$(ACTIONS_GROUP)
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" -q $(OPTS) $(TESTOPTS) $(BTESTS)
+ $(Q)$(gnumake_recursive)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
$(ACTIONS_ENDGROUP)
rtest: yes-fake miniruby$(EXEEXT) PHONY
@@ -812,7 +845,7 @@ yes-test-tool: prog PHONY
no-test-tool: PHONY
test-sample: test-basic # backward compatibility for mswin-build
-test-short: btest-ruby test-knownbug test-basic
+test-short: btest-ruby $(DOT_WAIT) test-knownbug $(DOT_WAIT) test-basic
test: test-short
# $ make test-all TESTOPTS="--help" displays more detail
@@ -839,7 +872,10 @@ extconf: $(PREP)
$(Q) $(MAKEDIRS) "$(EXTCONFDIR)"
$(RUNRUBY) -C "$(EXTCONFDIR)" $(EXTCONF) $(EXTCONFARGS)
-$(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h
+rbconfig.rb: $(RBCONFIG)
+
+$(HAVE_BASERUBY:no=)$(RBCONFIG)$(HAVE_BASERUBY:no=): $(PREP)
+$(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h $(srcdir)/common.mk
$(Q)$(BOOTSTRAPRUBY) -n \
-e 'BEGIN{version=ARGV.shift;mis=ARGV.dup}' \
-e 'END{abort "UNICODE version mismatch: #{mis}" unless mis.empty?}' \
@@ -857,7 +893,7 @@ $(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h
test-rubyspec: test-spec
yes-test-rubyspec: yes-test-spec
-test-spec-precheck: programs
+test-spec-precheck: programs yes-fake
test-spec: $(TEST_RUNNABLE)-test-spec
yes-test-spec: test-spec-precheck
@@ -867,6 +903,8 @@ yes-test-spec: test-spec-precheck
$(ACTIONS_ENDGROUP)
no-test-spec:
+check: $(DOT_WAIT) test-spec
+
RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable
runnable: $(RUNNABLE) prog $(tooldir)/mkrunnable.rb PHONY
$(Q) $(MINIRUBY) $(tooldir)/mkrunnable.rb -v $(EXTOUT)
@@ -885,9 +923,10 @@ libtrans trans: {$(VPATH)}transdb.h
ENC_HEADERS = $(srcdir)/enc/jis/props.h
# Use MINIRUBY which loads fake.rb for cross compiling
$(ENC_MK): $(srcdir)/enc/make_encmake.rb $(srcdir)/enc/Makefile.in $(srcdir)/enc/depend \
- $(srcdir)/enc/encinit.c.erb $(ENC_HEADERS) $(srcdir)/lib/mkmf.rb $(RBCONFIG) fake
+ $(srcdir)/enc/encinit.c.erb $(ENC_HEADERS) $(srcdir)/lib/mkmf.rb $(RBCONFIG) $(HAVE_BASERUBY)-fake
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/enc/make_encmake.rb --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" --module$(ENCSTATIC) $(ENCS) $@
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/enc/make_encmake.rb \
+ --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" --module$(ENCSTATIC) $(ENCS) $@
.PRECIOUS: $(MKFILES)
@@ -909,9 +948,7 @@ PHONY:
{$(srcdir)}.y.c:
$(ECHO) generating $@
- $(Q)$(BASERUBY) $(tooldir)/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y
- $(Q)$(BASERUBY) $(tooldir)/pure_parser.rb parse.tmp.y $(YACC)
- $(Q)$(RM) parse.tmp.y.bak
+ $(Q)$(BASERUBY) $(tooldir)/id2token.rb $(SRC_FILE) > parse.tmp.y
$(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y
$(Q)$(RM) parse.tmp.y
$(Q)sed -f $(tooldir)/ytab.sed -e "/^#/s|parse\.tmp\.[iy]|$(SRC_FILE)|" -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@.new
@@ -923,11 +960,11 @@ $(PLATFORM_D):
$(Q) $(MAKEDIRS) $(PLATFORM_DIR) $(@D)
@$(NULLCMD) > $@
-exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time miniruby$(EXEEXT) {$(VPATH)}config.h
+exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time $(PREP) {$(VPATH)}config.h
$(Q) $(CC) $(CFLAGS) $(INCFLAGS) $(CPPFLAGS) -DRUBY_INSTALL_NAME=$(@F) $(COUTFLAG)ruby-runner.$(OBJEXT) -c $(CSRCFLAG)$(srcdir)/ruby-runner.c
$(Q) $(PURIFY) $(CC) $(CFLAGS) $(LDFLAGS) $(OUTFLAG)$@ ruby-runner.$(OBJEXT) $(LIBS)
$(Q) $(POSTLINK)
- $(Q) ./miniruby$(EXEEXT) \
+ $(Q) $(BOOTSTRAPRUBY) \
-e 'prog, dest, inst = ARGV; dest += "/ruby"' \
-e 'exit unless prog==inst' \
-e 'unless prog=="ruby"' \
@@ -1041,11 +1078,7 @@ $(srcs_vpath)insns_info.inc: $(tooldir)/ruby_vm/views/insns_info.inc.erb $(inc_c
$(srcs_vpath)vmtc.inc: $(tooldir)/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
$(srcs_vpath)vm.inc: $(tooldir)/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
$(tooldir)/ruby_vm/views/_insn_entry.erb $(tooldir)/ruby_vm/views/_trace_instruction.erb
-$(srcs_vpath)mjit_compile.inc: $(tooldir)/ruby_vm/views/mjit_compile.inc.erb $(inc_common_headers) \
- $(tooldir)/ruby_vm/views/_mjit_compile_insn.erb $(tooldir)/ruby_vm/views/_mjit_compile_send.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_ivar.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_insn_body.erb $(tooldir)/ruby_vm/views/_mjit_compile_pc_and_sp.erb \
- $(tooldir)/ruby_vm/views/_mjit_compile_invokebuiltin.erb $(tooldir)/ruby_vm/views/_mjit_compile_getinlinecache.erb
+$(srcs_vpath)mjit_sp_inc.inc: $(tooldir)/ruby_vm/views/mjit_sp_inc.inc.erb
BUILTIN_RB_SRCS = \
$(srcdir)/ast.rb \
@@ -1055,12 +1088,14 @@ BUILTIN_RB_SRCS = \
$(srcdir)/io.rb \
$(srcdir)/marshal.rb \
$(srcdir)/mjit.rb \
+ $(srcdir)/mjit_c.rb \
$(srcdir)/pack.rb \
$(srcdir)/trace_point.rb \
$(srcdir)/warning.rb \
$(srcdir)/array.rb \
$(srcdir)/kernel.rb \
$(srcdir)/ractor.rb \
+ $(srcdir)/symbol.rb \
$(srcdir)/timev.rb \
$(srcdir)/thread_sync.rb \
$(srcdir)/nilclass.rb \
@@ -1072,7 +1107,7 @@ BUILTIN_RB_INCS = $(BUILTIN_RB_SRCS:.rb=.rbinc)
common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
$(BUILTIN_RB_INCS) \
- srcs-lib srcs-ext incs
+ srcs-lib srcs-ext incs preludes
missing-srcs: $(srcdir)/missing/des_tables.c
@@ -1128,13 +1163,13 @@ node_name.inc: $(tooldir)/node_name.rb $(srcdir)/node.h
$(ECHO) generating $@
$(Q) $(BASERUBY) -n $(tooldir)/node_name.rb < $(srcdir)/node.h > $@
-encdb.h: $(PREP) $(tooldir)/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
+encdb.h: $(RBCONFIG) $(tooldir)/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/encdb.h.tmpl $(srcdir)/enc enc
+ $(Q) $(BOOTSTRAPRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/encdb.h.tmpl $(srcdir)/enc enc
-transdb.h: $(PREP) srcs-enc $(tooldir)/generic_erb.rb $(srcdir)/template/transdb.h.tmpl
+transdb.h: $(RBCONFIG) srcs-enc $(tooldir)/generic_erb.rb $(srcdir)/template/transdb.h.tmpl
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/transdb.h.tmpl $(srcdir)/enc/trans enc/trans
+ $(Q) $(BOOTSTRAPRUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/transdb.h.tmpl $(srcdir)/enc/trans enc/trans
enc/encinit.c: $(ENC_MK) $(srcdir)/enc/encinit.c.erb
@@ -1184,25 +1219,19 @@ builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary
$(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb
-$(srcdir)/revision.h:
-$(srcdir)/revision.h$(gnumake:yes=-nongnumake):
- $(Q)$(RM) $(@F)
- $(Q)$(NULLCMD) > $@ || $(NULLCMD) > $(@F)
-
-revision.tmp::
- $(Q) $(NULLCMD) > $@
-revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE)
- $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" > $@
+$(srcdir)/revision.h$(no_baseruby:no=~disabled~): $(REVISION_H)
-$(REVISION_H): revision.tmp
- $(Q)$(IFCHANGE) "--timestamp=$@" "$(srcdir)/revision.h" revision.tmp
+$(REVISION_H)$(no_baseruby:no=~disabled~):
+ $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" --output=revision.h --timestamp=$@
+$(REVISION_H)$(yes_baseruby:yes=~disabled~):
+ $(Q) exit > $@
-$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y id.h $(srcdir)/ext/ripper/depend
+$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
- Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON=$(YACC) top_srcdir=../.. srcdir=. VPATH=../.. \
+ Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON="$(YACC)" top_srcdir=../.. srcdir=. VPATH=../.. \
RUBY="$(BASERUBY)" PATH_SEPARATOR="$(PATH_SEPARATOR)" LANG=C
$(srcdir)/ext/json/parser/parser.c: $(srcdir)/ext/json/parser/parser.rl $(srcdir)/ext/json/parser/prereq.mk
@@ -1219,7 +1248,7 @@ $(srcdir)/ext/rbconfig/sizeof/sizes.c: $(srcdir)/ext/rbconfig/sizeof/depend \
$(tooldir)/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.ac
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
@@ -1227,19 +1256,19 @@ $(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \
$(tooldir)/generic_erb.rb $(srcdir)/template/limits.c.tmpl
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
$(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)"
$(srcdir)/ext/etc/constdefs.h: $(srcdir)/ext/etc/depend
$(Q) $(CHDIR) $(@D) && \
- sed '/AUTOGENERATED/q' depend | \
+ $(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)"
@@ -1311,7 +1340,7 @@ lldb-ruby: $(PROGRAM) PHONY
DISTPKGS = gzip,zip,all
PKGSDIR = tmp
dist:
- $(BASERUBY) $(tooldir)/make-snapshot \
+ $(BASERUBY) $(V0:1=-v) $(tooldir)/make-snapshot \
-srcdir=$(srcdir) -packages=$(DISTPKGS) \
-unicode-version=$(UNICODE_VERSION) \
$(DISTOPTS) $(PKGSDIR) $(RELNAME)
@@ -1329,6 +1358,10 @@ after-update:: $(REVISION_H)
after-update:: extract-extlibs
after-update:: extract-gems
+update-src::
+ $(Q) $(RM) $(REVISION_H) revision.h "$(srcdir)/$(REVISION_H)" "$(srcdir)/revision.h"
+ $(Q) exit > "$(srcdir)/revision.h"
+
update-remote:: update-src update-download
update-download:: $(ALWAYS_UPDATE_UNICODE:yes=update-unicode)
update-download:: update-gems
@@ -1343,6 +1376,7 @@ update-config_files: PHONY
refresh-gems: update-bundled_gems prepare-gems
prepare-gems: $(HAVE_BASERUBY:yes=update-gems) $(HAVE_BASERUBY:yes=extract-gems)
+extract-gems: $(HAVE_BASERUBY:yes=update-gems)
update-gems$(gnumake:yes=-sequential): PHONY
$(ECHO) Downloading bundled gem files...
@@ -1374,6 +1408,9 @@ extract-gems$(gnumake:yes=-sequential): PHONY
-e 'end' \
gems/bundled_gems
+outdate-bundled-gems: PHONY
+ $(Q) $(BASERUBY) $(tooldir)/$@.rb --make="$(MAKE)" --mflags="$(MFLAGS)" "$(srcdir)"
+
update-bundled_gems: PHONY
$(Q) $(RUNRUBY) -rrubygems \
$(tooldir)/update-bundled_gems.rb \
@@ -1400,7 +1437,7 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck
yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck
$(ACTIONS_GROUP)
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "yard" "pry" "packnga" "rexml" "json-schema" "test-unit-rr"
+ --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "rexml" "json-schema:5.1.0" "test-unit-rr"
$(ACTIONS_ENDGROUP)
PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
@@ -1415,9 +1452,32 @@ BUNDLED_GEMS =
test-bundled-gems-run: $(PREPARE_BUNDLED_GEMS)
$(gnumake_recursive)$(Q) $(XRUBY) $(tooldir)/test-bundled-gems.rb $(BUNDLED_GEMS)
+test-syntax-suggest-precheck: $(TEST_RUNNABLE)-test-syntax-suggest-precheck
+no-test-syntax-suggest-precheck:
+yes-test-syntax-suggest-precheck: main
+
+test-syntax-suggest-prepare: $(TEST_RUNNABLE)-test-syntax-suggest-prepare
+no-test-syntax-suggest-prepare: no-test-syntax-suggest-precheck
+yes-test-syntax-suggest-prepare: yes-test-syntax-suggest-precheck
+ $(ACTIONS_GROUP)
+ $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
+ --install-dir .bundle --conservative "bundler" "rake" "rspec:~> 3" #"ruby-prof"
+ $(ACTIONS_ENDGROUP)
+
+RSPECOPTS =
+SYNTAX_SUGGEST_SPECS =
+PREPARE_SYNTAX_SUGGEST = test-syntax-suggest-prepare
+test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest
+yes-test-syntax-suggest: yes-$(PREPARE_SYNTAX_SUGGEST)
+ $(XRUBY) -C $(srcdir) -Ispec/syntax_suggest .bundle/bin/rspec \
+ --require spec_helper $(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS)
+no-test-syntax-suggest:
+
+check: $(DOT_WAIT) $(TEST_RUNNABLE)-$(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest
+
test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck
no-test-bundler-precheck:
-yes-test-bundler-precheck: main
+yes-test-bundler-precheck: main $(arch)-fake.rb
no-test-bundler-prepare: no-test-bundler-precheck
yes-test-bundler-prepare: yes-test-bundler-precheck
@@ -1434,14 +1494,18 @@ RSPECOPTS =
BUNDLER_SPECS =
test-bundler: $(TEST_RUNNABLE)-test-bundler
yes-test-bundler: yes-test-bundler-prepare
- $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \
+ $(gnumake_recursive)$(XRUBY) \
+ -r./$(arch)-fake \
+ -e "exec(*ARGV)" -- \
+ $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \
--require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS)
no-test-bundler:
PARALLELRSPECOPTS = --runtime-log $(srcdir)/tmp/parallel_runtime_rspec.log
test-bundler-parallel: $(TEST_RUNNABLE)-test-bundler-parallel
yes-test-bundler-parallel: yes-test-bundler-prepare
- $(XRUBY) \
+ $(gnumake_recursive)$(XRUBY) \
+ -r./$(arch)-fake \
-e "ARGV[-1] = File.expand_path(ARGV[-1])" \
-e "exec(*ARGV)" -- \
$(XRUBY) -I$(srcdir)/spec/bundler \
@@ -1500,60 +1564,54 @@ update-unicode: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES) \
$(UNICODE_AUXILIARY_FILES) $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES)
CACHE_DIR = $(srcdir)/.downloaded-cache
-UNICODE_DOWNLOAD = \
+UNICODE_DOWNLOADER_ALWAYS_UPDATE = $(ALWAYS_UPDATE_UNICODE:yes=--always)
+UNICODE_DOWNLOADER = \
$(BASERUBY) $(tooldir)/downloader.rb \
--cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ --exist $(UNICODE_DOWNLOADER_ALWAYS_UPDATE:no=) \
+ unicode --unicode-beta=$(UNICODE_BETA)
+UNICODE_DOWNLOAD = \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR) \
- -p $(UNICODE_VERSION)/ucd \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd
UNICODE_AUXILIARY_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR)/auxiliary \
- -p $(UNICODE_VERSION)/ucd/auxiliary \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd/auxiliary
UNICODE_UCD_EMOJI_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_DATA_DIR)/emoji \
- -p $(UNICODE_VERSION)/ucd/emoji \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p $(UNICODE_VERSION)/ucd/emoji
UNICODE_EMOJI_DOWNLOAD = \
- $(BASERUBY) $(tooldir)/downloader.rb \
- --cache-dir=$(CACHE_DIR) \
- --unicode-beta $(UNICODE_BETA) \
+ $(UNICODE_DOWNLOADER) \
-d $(UNICODE_SRC_EMOJI_DATA_DIR) \
- -p emoji/$(UNICODE_EMOJI_VERSION) \
- -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+ -p emoji/$(UNICODE_EMOJI_VERSION)
-$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES): update-unicode-files
-update-unicode-files:
+update-unicode-files: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES):
$(ECHO) Downloading Unicode $(UNICODE_VERSION) data and property files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)"
$(Q) $(UNICODE_DOWNLOAD) $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
-$(UNICODE_AUXILIARY_FILES): update-unicode-auxiliary-files
-update-unicode-auxiliary-files:
+update-unicode-auxiliary-files: $(UNICODE_AUXILIARY_FILES)
+$(UNICODE_AUXILIARY_FILES):
$(ECHO) Downloading Unicode $(UNICODE_VERSION) auxiliary files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)/auxiliary"
$(Q) $(UNICODE_AUXILIARY_DOWNLOAD) $(UNICODE_AUXILIARY_FILES)
-$(UNICODE_UCD_EMOJI_FILES): update-unicode-ucd-emoji-files
-update-unicode-ucd-emoji-files:
+update-unicode-ucd-emoji-files: $(UNICODE_UCD_EMOJI_FILES)
+$(UNICODE_UCD_EMOJI_FILES):
$(ECHO) Downloading Unicode UCD emoji $(UNICODE_EMOJI_VERSION) files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)/emoji"
$(Q) $(UNICODE_UCD_EMOJI_DOWNLOAD) $(UNICODE_UCD_EMOJI_FILES)
-$(UNICODE_EMOJI_FILES): update-unicode-emoji-files
-update-unicode-emoji-files:
+update-unicode-emoji-files: $(UNICODE_EMOJI_FILES)
+$(UNICODE_EMOJI_FILES):
$(ECHO) Downloading Unicode emoji $(UNICODE_EMOJI_VERSION) files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_EMOJI_DATA_DIR)"
$(Q) $(UNICODE_EMOJI_DOWNLOAD) $(UNICODE_EMOJI_FILES)
-$(srcdir)/lib/unicode_normalize/$(ALWAYS_UPDATE_UNICODE:yes=tables.rb): \
+$(srcdir)/lib/unicode_normalize/tables.rb: \
$(UNICODE_SRC_DATA_DIR)/$(HAVE_BASERUBY:yes=.unicode-tables.time)
$(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \
@@ -1562,13 +1620,25 @@ $(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \
touch-unicode-files:
$(MAKEDIRS) $(UNICODE_SRC_DATA_DIR)
- touch $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
+ $(Q) $(TOUCH) $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
+UNICODE_TABLES_DATA_FILES = \
+ $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
+ $(UNICODE_SRC_DATA_DIR)/CompositionExclusions.txt \
+ $(empty)
+
+UNICODE_TABLES_DEPENDENTS_1 = none$(ALWAYS_UPDATE_UNICODE)
+UNICODE_TABLES_DEPENDENTS = $(UNICODE_TABLES_DEPENDENTS_1:noneyes=force)
UNICODE_TABLES_TIMESTAMP = yes
-$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: $(tooldir)/generic_erb.rb \
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:none=time):
+ $(Q) $(MAKEDIRS) $(@D)
+ $(Q) exit > $(@) || $(NULLCMD)
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:force=time): \
+ $(tooldir)/generic_erb.rb \
$(srcdir)/template/unicode_norm_gen.tmpl \
- $(ALWAYS_UPDATE_UNICODE:yes=update-unicode)
- $(Q) $(MAKE) $(@D)
+ $(UNICODE_TABLES_DATA_FILES) \
+ $(order_only) \
+ $(UNICODE_SRC_DATA_DIR)
$(Q) $(BASERUBY) $(tooldir)/generic_erb.rb \
-c $(UNICODE_TABLES_TIMESTAMP:yes=-t$@) \
-o $(srcdir)/lib/unicode_normalize/tables.rb \
@@ -1594,19 +1664,19 @@ $(UNICODE_HDR_DIR)/name2ctype.h:
$(MV) $@.new $@
# the next non-comment line was:
-# $(UNICODE_HDR_DIR)/casefold.h: $(srcdir)/enc/unicode/case-folding.rb \
+# $(UNICODE_HDR_DIR)/casefold.h: $(tooldir)/enc-case-folding.rb \
# but was changed to make sure CI works on systems that don't have gperf
unicode-up: $(UNICODE_DATA_HEADERS)
$(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=casefold.h): \
- $(srcdir)/enc/unicode/case-folding.rb \
+ $(tooldir)/enc-case-folding.rb \
$(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
$(UNICODE_SRC_DATA_DIR)/SpecialCasing.txt \
$(UNICODE_SRC_DATA_DIR)/CaseFolding.txt
$(UNICODE_HDR_DIR)/casefold.h:
$(MAKEDIRS) $(@D)
- $(Q) $(BASERUBY) $(srcdir)/enc/unicode/case-folding.rb \
+ $(Q) $(BASERUBY) $(tooldir)/enc-case-folding.rb \
--output-file=$@ \
--mapping-data-directory=$(UNICODE_SRC_DATA_DIR)
@@ -1702,6 +1772,9 @@ help: PHONY
" https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto" \
$(MESSAGE_END)
+$(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}mini_builtin.c
+$(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}miniprelude.c
+
# AUTOGENERATED DEPENDENCIES START
addr2line.$(OBJEXT): {$(VPATH)}addr2line.c
addr2line.$(OBJEXT): {$(VPATH)}addr2line.h
@@ -1759,6 +1832,7 @@ addr2line.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
addr2line.$(OBJEXT): {$(VPATH)}missing.h
array.$(OBJEXT): $(hdrdir)/ruby/ruby.h
array.$(OBJEXT): $(top_srcdir)/internal/array.h
+array.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
array.$(OBJEXT): $(top_srcdir)/internal/bignum.h
array.$(OBJEXT): $(top_srcdir)/internal/bits.h
array.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -1774,6 +1848,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/proc.h
array.$(OBJEXT): $(top_srcdir)/internal/rational.h
array.$(OBJEXT): $(top_srcdir)/internal/serial.h
array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+array.$(OBJEXT): $(top_srcdir)/internal/variable.h
array.$(OBJEXT): $(top_srcdir)/internal/vm.h
array.$(OBJEXT): $(top_srcdir)/internal/warnings.h
array.$(OBJEXT): {$(VPATH)}array.c
@@ -1790,6 +1865,7 @@ array.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
array.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
array.$(OBJEXT): {$(VPATH)}builtin.h
array.$(OBJEXT): {$(VPATH)}config.h
+array.$(OBJEXT): {$(VPATH)}constant.h
array.$(OBJEXT): {$(VPATH)}debug_counter.h
array.$(OBJEXT): {$(VPATH)}defines.h
array.$(OBJEXT): {$(VPATH)}encoding.h
@@ -1952,6 +2028,7 @@ array.$(OBJEXT): {$(VPATH)}oniguruma.h
array.$(OBJEXT): {$(VPATH)}probes.dmyh
array.$(OBJEXT): {$(VPATH)}probes.h
array.$(OBJEXT): {$(VPATH)}ruby_assert.h
+array.$(OBJEXT): {$(VPATH)}shape.h
array.$(OBJEXT): {$(VPATH)}st.h
array.$(OBJEXT): {$(VPATH)}subst.h
array.$(OBJEXT): {$(VPATH)}transient_heap.h
@@ -1963,6 +2040,7 @@ ast.$(OBJEXT): $(CCAN_DIR)/str/str.h
ast.$(OBJEXT): $(hdrdir)/ruby.h
ast.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ast.$(OBJEXT): $(top_srcdir)/internal/array.h
+ast.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ast.$(OBJEXT): $(top_srcdir)/internal/compilers.h
ast.$(OBJEXT): $(top_srcdir)/internal/gc.h
ast.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -1970,6 +2048,7 @@ ast.$(OBJEXT): $(top_srcdir)/internal/parse.h
ast.$(OBJEXT): $(top_srcdir)/internal/serial.h
ast.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+ast.$(OBJEXT): $(top_srcdir)/internal/variable.h
ast.$(OBJEXT): $(top_srcdir)/internal/vm.h
ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h
ast.$(OBJEXT): {$(VPATH)}assert.h
@@ -1987,9 +2066,11 @@ ast.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
ast.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
ast.$(OBJEXT): {$(VPATH)}builtin.h
ast.$(OBJEXT): {$(VPATH)}config.h
+ast.$(OBJEXT): {$(VPATH)}constant.h
ast.$(OBJEXT): {$(VPATH)}defines.h
ast.$(OBJEXT): {$(VPATH)}encoding.h
ast.$(OBJEXT): {$(VPATH)}id.h
+ast.$(OBJEXT): {$(VPATH)}id_table.h
ast.$(OBJEXT): {$(VPATH)}intern.h
ast.$(OBJEXT): {$(VPATH)}internal.h
ast.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -2149,6 +2230,7 @@ ast.$(OBJEXT): {$(VPATH)}onigmo.h
ast.$(OBJEXT): {$(VPATH)}oniguruma.h
ast.$(OBJEXT): {$(VPATH)}ruby_assert.h
ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ast.$(OBJEXT): {$(VPATH)}shape.h
ast.$(OBJEXT): {$(VPATH)}st.h
ast.$(OBJEXT): {$(VPATH)}subst.h
ast.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2332,6 +2414,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/warning_push.h
bignum.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
bignum.$(OBJEXT): {$(VPATH)}missing.h
bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h
+bignum.$(OBJEXT): {$(VPATH)}shape.h
bignum.$(OBJEXT): {$(VPATH)}st.h
bignum.$(OBJEXT): {$(VPATH)}subst.h
bignum.$(OBJEXT): {$(VPATH)}thread.h
@@ -2342,11 +2425,13 @@ builtin.$(OBJEXT): $(CCAN_DIR)/list/list.h
builtin.$(OBJEXT): $(CCAN_DIR)/str/str.h
builtin.$(OBJEXT): $(hdrdir)/ruby/ruby.h
builtin.$(OBJEXT): $(top_srcdir)/internal/array.h
+builtin.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
builtin.$(OBJEXT): $(top_srcdir)/internal/compilers.h
builtin.$(OBJEXT): $(top_srcdir)/internal/gc.h
builtin.$(OBJEXT): $(top_srcdir)/internal/imemo.h
builtin.$(OBJEXT): $(top_srcdir)/internal/serial.h
builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h
builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h
builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h
builtin.$(OBJEXT): {$(VPATH)}assert.h
@@ -2364,8 +2449,10 @@ builtin.$(OBJEXT): {$(VPATH)}builtin.c
builtin.$(OBJEXT): {$(VPATH)}builtin.h
builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc
builtin.$(OBJEXT): {$(VPATH)}config.h
+builtin.$(OBJEXT): {$(VPATH)}constant.h
builtin.$(OBJEXT): {$(VPATH)}defines.h
builtin.$(OBJEXT): {$(VPATH)}id.h
+builtin.$(OBJEXT): {$(VPATH)}id_table.h
builtin.$(OBJEXT): {$(VPATH)}intern.h
builtin.$(OBJEXT): {$(VPATH)}internal.h
builtin.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -2514,6 +2601,7 @@ builtin.$(OBJEXT): {$(VPATH)}missing.h
builtin.$(OBJEXT): {$(VPATH)}node.h
builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h
builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+builtin.$(OBJEXT): {$(VPATH)}shape.h
builtin.$(OBJEXT): {$(VPATH)}st.h
builtin.$(OBJEXT): {$(VPATH)}subst.h
builtin.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2526,6 +2614,7 @@ class.$(OBJEXT): $(CCAN_DIR)/list/list.h
class.$(OBJEXT): $(CCAN_DIR)/str/str.h
class.$(OBJEXT): $(hdrdir)/ruby/ruby.h
class.$(OBJEXT): $(top_srcdir)/internal/array.h
+class.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
class.$(OBJEXT): $(top_srcdir)/internal/class.h
class.$(OBJEXT): $(top_srcdir)/internal/compilers.h
class.$(OBJEXT): $(top_srcdir)/internal/eval.h
@@ -2716,6 +2805,7 @@ class.$(OBJEXT): {$(VPATH)}onigmo.h
class.$(OBJEXT): {$(VPATH)}oniguruma.h
class.$(OBJEXT): {$(VPATH)}ruby_assert.h
class.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+class.$(OBJEXT): {$(VPATH)}shape.h
class.$(OBJEXT): {$(VPATH)}st.h
class.$(OBJEXT): {$(VPATH)}subst.h
class.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -2723,6 +2813,7 @@ class.$(OBJEXT): {$(VPATH)}thread_native.h
class.$(OBJEXT): {$(VPATH)}vm_core.h
class.$(OBJEXT): {$(VPATH)}vm_opts.h
compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+compar.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compar.$(OBJEXT): $(top_srcdir)/internal/compar.h
compar.$(OBJEXT): $(top_srcdir)/internal/compilers.h
compar.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -2907,6 +2998,7 @@ compile.$(OBJEXT): $(CCAN_DIR)/list/list.h
compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
compile.$(OBJEXT): $(top_srcdir)/internal/array.h
+compile.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compile.$(OBJEXT): $(top_srcdir)/internal/bignum.h
compile.$(OBJEXT): $(top_srcdir)/internal/bits.h
compile.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -3119,6 +3211,7 @@ compile.$(OBJEXT): {$(VPATH)}re.h
compile.$(OBJEXT): {$(VPATH)}regex.h
compile.$(OBJEXT): {$(VPATH)}ruby_assert.h
compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+compile.$(OBJEXT): {$(VPATH)}shape.h
compile.$(OBJEXT): {$(VPATH)}st.h
compile.$(OBJEXT): {$(VPATH)}subst.h
compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -3128,8 +3221,13 @@ compile.$(OBJEXT): {$(VPATH)}vm_callinfo.h
compile.$(OBJEXT): {$(VPATH)}vm_core.h
compile.$(OBJEXT): {$(VPATH)}vm_debug.h
compile.$(OBJEXT): {$(VPATH)}vm_opts.h
+complex.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+complex.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+complex.$(OBJEXT): $(CCAN_DIR)/list/list.h
+complex.$(OBJEXT): $(CCAN_DIR)/str/str.h
complex.$(OBJEXT): $(hdrdir)/ruby/ruby.h
complex.$(OBJEXT): $(top_srcdir)/internal/array.h
+complex.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
complex.$(OBJEXT): $(top_srcdir)/internal/bignum.h
complex.$(OBJEXT): $(top_srcdir)/internal/bits.h
complex.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -3137,15 +3235,18 @@ complex.$(OBJEXT): $(top_srcdir)/internal/compilers.h
complex.$(OBJEXT): $(top_srcdir)/internal/complex.h
complex.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
complex.$(OBJEXT): $(top_srcdir)/internal/gc.h
+complex.$(OBJEXT): $(top_srcdir)/internal/imemo.h
complex.$(OBJEXT): $(top_srcdir)/internal/math.h
complex.$(OBJEXT): $(top_srcdir)/internal/numeric.h
complex.$(OBJEXT): $(top_srcdir)/internal/object.h
complex.$(OBJEXT): $(top_srcdir)/internal/rational.h
complex.$(OBJEXT): $(top_srcdir)/internal/serial.h
complex.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+complex.$(OBJEXT): $(top_srcdir)/internal/variable.h
complex.$(OBJEXT): $(top_srcdir)/internal/vm.h
complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h
complex.$(OBJEXT): {$(VPATH)}assert.h
+complex.$(OBJEXT): {$(VPATH)}atomic.h
complex.$(OBJEXT): {$(VPATH)}backward/2/assume.h
complex.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
complex.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -3157,6 +3258,7 @@ complex.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
complex.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
complex.$(OBJEXT): {$(VPATH)}complex.c
complex.$(OBJEXT): {$(VPATH)}config.h
+complex.$(OBJEXT): {$(VPATH)}constant.h
complex.$(OBJEXT): {$(VPATH)}defines.h
complex.$(OBJEXT): {$(VPATH)}id.h
complex.$(OBJEXT): {$(VPATH)}id_table.h
@@ -3302,10 +3404,18 @@ complex.$(OBJEXT): {$(VPATH)}internal/value_type.h
complex.$(OBJEXT): {$(VPATH)}internal/variable.h
complex.$(OBJEXT): {$(VPATH)}internal/warning_push.h
complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+complex.$(OBJEXT): {$(VPATH)}method.h
complex.$(OBJEXT): {$(VPATH)}missing.h
+complex.$(OBJEXT): {$(VPATH)}node.h
complex.$(OBJEXT): {$(VPATH)}ruby_assert.h
+complex.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+complex.$(OBJEXT): {$(VPATH)}shape.h
complex.$(OBJEXT): {$(VPATH)}st.h
complex.$(OBJEXT): {$(VPATH)}subst.h
+complex.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+complex.$(OBJEXT): {$(VPATH)}thread_native.h
+complex.$(OBJEXT): {$(VPATH)}vm_core.h
+complex.$(OBJEXT): {$(VPATH)}vm_opts.h
cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
cont.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
cont.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -3313,13 +3423,18 @@ cont.$(OBJEXT): $(CCAN_DIR)/str/str.h
cont.$(OBJEXT): $(hdrdir)/ruby.h
cont.$(OBJEXT): $(hdrdir)/ruby/ruby.h
cont.$(OBJEXT): $(top_srcdir)/internal/array.h
+cont.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
cont.$(OBJEXT): $(top_srcdir)/internal/compilers.h
cont.$(OBJEXT): $(top_srcdir)/internal/cont.h
+cont.$(OBJEXT): $(top_srcdir)/internal/error.h
cont.$(OBJEXT): $(top_srcdir)/internal/gc.h
cont.$(OBJEXT): $(top_srcdir)/internal/imemo.h
cont.$(OBJEXT): $(top_srcdir)/internal/proc.h
+cont.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
cont.$(OBJEXT): $(top_srcdir)/internal/serial.h
cont.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+cont.$(OBJEXT): $(top_srcdir)/internal/string.h
+cont.$(OBJEXT): $(top_srcdir)/internal/variable.h
cont.$(OBJEXT): $(top_srcdir)/internal/vm.h
cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h
cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
@@ -3335,9 +3450,11 @@ cont.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
cont.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
cont.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
cont.$(OBJEXT): {$(VPATH)}config.h
+cont.$(OBJEXT): {$(VPATH)}constant.h
cont.$(OBJEXT): {$(VPATH)}cont.c
cont.$(OBJEXT): {$(VPATH)}debug_counter.h
cont.$(OBJEXT): {$(VPATH)}defines.h
+cont.$(OBJEXT): {$(VPATH)}encoding.h
cont.$(OBJEXT): {$(VPATH)}eval_intern.h
cont.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
cont.$(OBJEXT): {$(VPATH)}gc.h
@@ -3415,6 +3532,15 @@ cont.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
cont.$(OBJEXT): {$(VPATH)}internal/ctype.h
cont.$(OBJEXT): {$(VPATH)}internal/dllexport.h
cont.$(OBJEXT): {$(VPATH)}internal/dosish.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+cont.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
cont.$(OBJEXT): {$(VPATH)}internal/error.h
cont.$(OBJEXT): {$(VPATH)}internal/eval.h
cont.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -3485,14 +3611,18 @@ cont.$(OBJEXT): {$(VPATH)}internal/value_type.h
cont.$(OBJEXT): {$(VPATH)}internal/variable.h
cont.$(OBJEXT): {$(VPATH)}internal/warning_push.h
cont.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+cont.$(OBJEXT): {$(VPATH)}iseq.h
cont.$(OBJEXT): {$(VPATH)}method.h
cont.$(OBJEXT): {$(VPATH)}missing.h
cont.$(OBJEXT): {$(VPATH)}mjit.h
cont.$(OBJEXT): {$(VPATH)}node.h
+cont.$(OBJEXT): {$(VPATH)}onigmo.h
+cont.$(OBJEXT): {$(VPATH)}oniguruma.h
cont.$(OBJEXT): {$(VPATH)}ractor.h
cont.$(OBJEXT): {$(VPATH)}ractor_core.h
cont.$(OBJEXT): {$(VPATH)}ruby_assert.h
cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+cont.$(OBJEXT): {$(VPATH)}shape.h
cont.$(OBJEXT): {$(VPATH)}st.h
cont.$(OBJEXT): {$(VPATH)}subst.h
cont.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -3500,12 +3630,15 @@ cont.$(OBJEXT): {$(VPATH)}thread_native.h
cont.$(OBJEXT): {$(VPATH)}vm_core.h
cont.$(OBJEXT): {$(VPATH)}vm_debug.h
cont.$(OBJEXT): {$(VPATH)}vm_opts.h
+cont.$(OBJEXT): {$(VPATH)}vm_sync.h
+cont.$(OBJEXT): {$(VPATH)}yjit.h
debug.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
debug.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
debug.$(OBJEXT): $(CCAN_DIR)/list/list.h
debug.$(OBJEXT): $(CCAN_DIR)/str/str.h
debug.$(OBJEXT): $(hdrdir)/ruby/ruby.h
debug.$(OBJEXT): $(top_srcdir)/internal/array.h
+debug.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
debug.$(OBJEXT): $(top_srcdir)/internal/class.h
debug.$(OBJEXT): $(top_srcdir)/internal/compilers.h
debug.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -3513,6 +3646,7 @@ debug.$(OBJEXT): $(top_srcdir)/internal/imemo.h
debug.$(OBJEXT): $(top_srcdir)/internal/serial.h
debug.$(OBJEXT): $(top_srcdir)/internal/signal.h
debug.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+debug.$(OBJEXT): $(top_srcdir)/internal/variable.h
debug.$(OBJEXT): $(top_srcdir)/internal/vm.h
debug.$(OBJEXT): $(top_srcdir)/internal/warnings.h
debug.$(OBJEXT): {$(VPATH)}assert.h
@@ -3527,6 +3661,7 @@ debug.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
debug.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
debug.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
debug.$(OBJEXT): {$(VPATH)}config.h
+debug.$(OBJEXT): {$(VPATH)}constant.h
debug.$(OBJEXT): {$(VPATH)}debug.c
debug.$(OBJEXT): {$(VPATH)}debug_counter.h
debug.$(OBJEXT): {$(VPATH)}defines.h
@@ -3697,6 +3832,7 @@ debug.$(OBJEXT): {$(VPATH)}ractor.h
debug.$(OBJEXT): {$(VPATH)}ractor_core.h
debug.$(OBJEXT): {$(VPATH)}ruby_assert.h
debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+debug.$(OBJEXT): {$(VPATH)}shape.h
debug.$(OBJEXT): {$(VPATH)}st.h
debug.$(OBJEXT): {$(VPATH)}subst.h
debug.$(OBJEXT): {$(VPATH)}symbol.h
@@ -3881,6 +4017,7 @@ dir.$(OBJEXT): $(top_srcdir)/internal/object.h
dir.$(OBJEXT): $(top_srcdir)/internal/serial.h
dir.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
dir.$(OBJEXT): $(top_srcdir)/internal/string.h
+dir.$(OBJEXT): $(top_srcdir)/internal/variable.h
dir.$(OBJEXT): $(top_srcdir)/internal/vm.h
dir.$(OBJEXT): $(top_srcdir)/internal/warnings.h
dir.$(OBJEXT): {$(VPATH)}assert.h
@@ -3895,6 +4032,7 @@ dir.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
dir.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
dir.$(OBJEXT): {$(VPATH)}builtin.h
dir.$(OBJEXT): {$(VPATH)}config.h
+dir.$(OBJEXT): {$(VPATH)}constant.h
dir.$(OBJEXT): {$(VPATH)}defines.h
dir.$(OBJEXT): {$(VPATH)}dir.c
dir.$(OBJEXT): {$(VPATH)}dir.rbinc
@@ -4057,6 +4195,7 @@ dir.$(OBJEXT): {$(VPATH)}io.h
dir.$(OBJEXT): {$(VPATH)}missing.h
dir.$(OBJEXT): {$(VPATH)}onigmo.h
dir.$(OBJEXT): {$(VPATH)}oniguruma.h
+dir.$(OBJEXT): {$(VPATH)}shape.h
dir.$(OBJEXT): {$(VPATH)}st.h
dir.$(OBJEXT): {$(VPATH)}subst.h
dir.$(OBJEXT): {$(VPATH)}thread.h
@@ -5374,6 +5513,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/class.h
encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h
encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h
encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/error.h
encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h
encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h
encoding.$(OBJEXT): $(top_srcdir)/internal/load.h
@@ -5381,6 +5521,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/object.h
encoding.$(OBJEXT): $(top_srcdir)/internal/serial.h
encoding.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
encoding.$(OBJEXT): $(top_srcdir)/internal/string.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h
encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h
encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h
encoding.$(OBJEXT): {$(VPATH)}assert.h
@@ -5394,6 +5535,7 @@ encoding.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
encoding.$(OBJEXT): {$(VPATH)}config.h
+encoding.$(OBJEXT): {$(VPATH)}constant.h
encoding.$(OBJEXT): {$(VPATH)}debug_counter.h
encoding.$(OBJEXT): {$(VPATH)}defines.h
encoding.$(OBJEXT): {$(VPATH)}encindex.h
@@ -5556,6 +5698,7 @@ encoding.$(OBJEXT): {$(VPATH)}onigmo.h
encoding.$(OBJEXT): {$(VPATH)}oniguruma.h
encoding.$(OBJEXT): {$(VPATH)}regenc.h
encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h
+encoding.$(OBJEXT): {$(VPATH)}shape.h
encoding.$(OBJEXT): {$(VPATH)}st.h
encoding.$(OBJEXT): {$(VPATH)}subst.h
encoding.$(OBJEXT): {$(VPATH)}util.h
@@ -5563,6 +5706,7 @@ encoding.$(OBJEXT): {$(VPATH)}vm_debug.h
encoding.$(OBJEXT): {$(VPATH)}vm_sync.h
enum.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enum.$(OBJEXT): $(top_srcdir)/internal/array.h
+enum.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
enum.$(OBJEXT): $(top_srcdir)/internal/bignum.h
enum.$(OBJEXT): $(top_srcdir)/internal/bits.h
enum.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -5580,6 +5724,7 @@ enum.$(OBJEXT): $(top_srcdir)/internal/rational.h
enum.$(OBJEXT): $(top_srcdir)/internal/re.h
enum.$(OBJEXT): $(top_srcdir)/internal/serial.h
enum.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+enum.$(OBJEXT): $(top_srcdir)/internal/variable.h
enum.$(OBJEXT): $(top_srcdir)/internal/vm.h
enum.$(OBJEXT): $(top_srcdir)/internal/warnings.h
enum.$(OBJEXT): {$(VPATH)}assert.h
@@ -5593,6 +5738,7 @@ enum.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
enum.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
enum.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
enum.$(OBJEXT): {$(VPATH)}config.h
+enum.$(OBJEXT): {$(VPATH)}constant.h
enum.$(OBJEXT): {$(VPATH)}defines.h
enum.$(OBJEXT): {$(VPATH)}encoding.h
enum.$(OBJEXT): {$(VPATH)}enum.c
@@ -5753,14 +5899,21 @@ enum.$(OBJEXT): {$(VPATH)}missing.h
enum.$(OBJEXT): {$(VPATH)}onigmo.h
enum.$(OBJEXT): {$(VPATH)}oniguruma.h
enum.$(OBJEXT): {$(VPATH)}ruby_assert.h
+enum.$(OBJEXT): {$(VPATH)}shape.h
enum.$(OBJEXT): {$(VPATH)}st.h
enum.$(OBJEXT): {$(VPATH)}subst.h
enum.$(OBJEXT): {$(VPATH)}symbol.h
enum.$(OBJEXT): {$(VPATH)}util.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/list/list.h
+enumerator.$(OBJEXT): $(CCAN_DIR)/str/str.h
enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/array.h
+enumerator.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/bignum.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/bits.h
+enumerator.$(OBJEXT): $(top_srcdir)/internal/class.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/compilers.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/enumerator.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -5778,6 +5931,7 @@ enumerator.$(OBJEXT): $(top_srcdir)/internal/struct.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/vm.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/warnings.h
enumerator.$(OBJEXT): {$(VPATH)}assert.h
+enumerator.$(OBJEXT): {$(VPATH)}atomic.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/assume.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -5792,6 +5946,7 @@ enumerator.$(OBJEXT): {$(VPATH)}defines.h
enumerator.$(OBJEXT): {$(VPATH)}encoding.h
enumerator.$(OBJEXT): {$(VPATH)}enumerator.c
enumerator.$(OBJEXT): {$(VPATH)}id.h
+enumerator.$(OBJEXT): {$(VPATH)}id_table.h
enumerator.$(OBJEXT): {$(VPATH)}intern.h
enumerator.$(OBJEXT): {$(VPATH)}internal.h
enumerator.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -5943,18 +6098,27 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/value_type.h
enumerator.$(OBJEXT): {$(VPATH)}internal/variable.h
enumerator.$(OBJEXT): {$(VPATH)}internal/warning_push.h
enumerator.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+enumerator.$(OBJEXT): {$(VPATH)}method.h
enumerator.$(OBJEXT): {$(VPATH)}missing.h
+enumerator.$(OBJEXT): {$(VPATH)}node.h
enumerator.$(OBJEXT): {$(VPATH)}onigmo.h
enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h
enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h
+enumerator.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+enumerator.$(OBJEXT): {$(VPATH)}shape.h
enumerator.$(OBJEXT): {$(VPATH)}st.h
enumerator.$(OBJEXT): {$(VPATH)}subst.h
+enumerator.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+enumerator.$(OBJEXT): {$(VPATH)}thread_native.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_core.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_opts.h
error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
error.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
error.$(OBJEXT): $(CCAN_DIR)/list/list.h
error.$(OBJEXT): $(CCAN_DIR)/str/str.h
error.$(OBJEXT): $(hdrdir)/ruby/ruby.h
error.$(OBJEXT): $(top_srcdir)/internal/array.h
+error.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
error.$(OBJEXT): $(top_srcdir)/internal/class.h
error.$(OBJEXT): $(top_srcdir)/internal/compilers.h
error.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -6152,6 +6316,7 @@ error.$(OBJEXT): {$(VPATH)}onigmo.h
error.$(OBJEXT): {$(VPATH)}oniguruma.h
error.$(OBJEXT): {$(VPATH)}ruby_assert.h
error.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+error.$(OBJEXT): {$(VPATH)}shape.h
error.$(OBJEXT): {$(VPATH)}st.h
error.$(OBJEXT): {$(VPATH)}subst.h
error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -6166,8 +6331,10 @@ eval.$(OBJEXT): $(CCAN_DIR)/str/str.h
eval.$(OBJEXT): $(hdrdir)/ruby.h
eval.$(OBJEXT): $(hdrdir)/ruby/ruby.h
eval.$(OBJEXT): $(top_srcdir)/internal/array.h
+eval.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
eval.$(OBJEXT): $(top_srcdir)/internal/class.h
eval.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+eval.$(OBJEXT): $(top_srcdir)/internal/cont.h
eval.$(OBJEXT): $(top_srcdir)/internal/error.h
eval.$(OBJEXT): $(top_srcdir)/internal/eval.h
eval.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -6373,6 +6540,7 @@ eval.$(OBJEXT): {$(VPATH)}ractor.h
eval.$(OBJEXT): {$(VPATH)}ractor_core.h
eval.$(OBJEXT): {$(VPATH)}ruby_assert.h
eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+eval.$(OBJEXT): {$(VPATH)}shape.h
eval.$(OBJEXT): {$(VPATH)}st.h
eval.$(OBJEXT): {$(VPATH)}subst.h
eval.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -6413,6 +6581,7 @@ file.$(OBJEXT): $(top_srcdir)/internal/serial.h
file.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
file.$(OBJEXT): $(top_srcdir)/internal/string.h
file.$(OBJEXT): $(top_srcdir)/internal/thread.h
+file.$(OBJEXT): $(top_srcdir)/internal/variable.h
file.$(OBJEXT): $(top_srcdir)/internal/vm.h
file.$(OBJEXT): $(top_srcdir)/internal/warnings.h
file.$(OBJEXT): {$(VPATH)}assert.h
@@ -6426,6 +6595,7 @@ file.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
file.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
file.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
file.$(OBJEXT): {$(VPATH)}config.h
+file.$(OBJEXT): {$(VPATH)}constant.h
file.$(OBJEXT): {$(VPATH)}defines.h
file.$(OBJEXT): {$(VPATH)}dln.h
file.$(OBJEXT): {$(VPATH)}encindex.h
@@ -6588,6 +6758,7 @@ file.$(OBJEXT): {$(VPATH)}io.h
file.$(OBJEXT): {$(VPATH)}missing.h
file.$(OBJEXT): {$(VPATH)}onigmo.h
file.$(OBJEXT): {$(VPATH)}oniguruma.h
+file.$(OBJEXT): {$(VPATH)}shape.h
file.$(OBJEXT): {$(VPATH)}st.h
file.$(OBJEXT): {$(VPATH)}subst.h
file.$(OBJEXT): {$(VPATH)}thread.h
@@ -6599,6 +6770,7 @@ gc.$(OBJEXT): $(CCAN_DIR)/str/str.h
gc.$(OBJEXT): $(hdrdir)/ruby.h
gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
gc.$(OBJEXT): $(top_srcdir)/internal/array.h
+gc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
gc.$(OBJEXT): $(top_srcdir)/internal/bignum.h
gc.$(OBJEXT): $(top_srcdir)/internal/bits.h
gc.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -6647,7 +6819,6 @@ gc.$(OBJEXT): {$(VPATH)}encoding.h
gc.$(OBJEXT): {$(VPATH)}eval_intern.h
gc.$(OBJEXT): {$(VPATH)}gc.c
gc.$(OBJEXT): {$(VPATH)}gc.h
-gc.$(OBJEXT): {$(VPATH)}gc.rb
gc.$(OBJEXT): {$(VPATH)}gc.rbinc
gc.$(OBJEXT): {$(VPATH)}id.h
gc.$(OBJEXT): {$(VPATH)}id_table.h
@@ -6804,6 +6975,7 @@ gc.$(OBJEXT): {$(VPATH)}internal/variable.h
gc.$(OBJEXT): {$(VPATH)}internal/warning_push.h
gc.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
gc.$(OBJEXT): {$(VPATH)}io.h
+gc.$(OBJEXT): {$(VPATH)}iseq.h
gc.$(OBJEXT): {$(VPATH)}method.h
gc.$(OBJEXT): {$(VPATH)}missing.h
gc.$(OBJEXT): {$(VPATH)}mjit.h
@@ -6820,6 +6992,7 @@ gc.$(OBJEXT): {$(VPATH)}regex.h
gc.$(OBJEXT): {$(VPATH)}regint.h
gc.$(OBJEXT): {$(VPATH)}ruby_assert.h
gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+gc.$(OBJEXT): {$(VPATH)}shape.h
gc.$(OBJEXT): {$(VPATH)}st.h
gc.$(OBJEXT): {$(VPATH)}subst.h
gc.$(OBJEXT): {$(VPATH)}symbol.h
@@ -6840,11 +7013,13 @@ goruby.$(OBJEXT): $(CCAN_DIR)/str/str.h
goruby.$(OBJEXT): $(hdrdir)/ruby.h
goruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
goruby.$(OBJEXT): $(top_srcdir)/internal/array.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
goruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h
goruby.$(OBJEXT): $(top_srcdir)/internal/serial.h
goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h
goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h
goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h
goruby.$(OBJEXT): {$(VPATH)}assert.h
@@ -6860,11 +7035,12 @@ goruby.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
goruby.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
goruby.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
goruby.$(OBJEXT): {$(VPATH)}config.h
+goruby.$(OBJEXT): {$(VPATH)}constant.h
goruby.$(OBJEXT): {$(VPATH)}defines.h
goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c
-goruby.$(OBJEXT): {$(VPATH)}golf_prelude.rb
goruby.$(OBJEXT): {$(VPATH)}goruby.c
goruby.$(OBJEXT): {$(VPATH)}id.h
+goruby.$(OBJEXT): {$(VPATH)}id_table.h
goruby.$(OBJEXT): {$(VPATH)}intern.h
goruby.$(OBJEXT): {$(VPATH)}internal.h
goruby.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -6904,6 +7080,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -7014,6 +7191,7 @@ goruby.$(OBJEXT): {$(VPATH)}missing.h
goruby.$(OBJEXT): {$(VPATH)}node.h
goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+goruby.$(OBJEXT): {$(VPATH)}shape.h
goruby.$(OBJEXT): {$(VPATH)}st.h
goruby.$(OBJEXT): {$(VPATH)}subst.h
goruby.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -7021,8 +7199,13 @@ goruby.$(OBJEXT): {$(VPATH)}thread_native.h
goruby.$(OBJEXT): {$(VPATH)}vm_core.h
goruby.$(OBJEXT): {$(VPATH)}vm_debug.h
goruby.$(OBJEXT): {$(VPATH)}vm_opts.h
+hash.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+hash.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+hash.$(OBJEXT): $(CCAN_DIR)/list/list.h
+hash.$(OBJEXT): $(CCAN_DIR)/str/str.h
hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h
hash.$(OBJEXT): $(top_srcdir)/internal/array.h
+hash.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
hash.$(OBJEXT): $(top_srcdir)/internal/bignum.h
hash.$(OBJEXT): $(top_srcdir)/internal/bits.h
hash.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -7031,6 +7214,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/cont.h
hash.$(OBJEXT): $(top_srcdir)/internal/error.h
hash.$(OBJEXT): $(top_srcdir)/internal/gc.h
hash.$(OBJEXT): $(top_srcdir)/internal/hash.h
+hash.$(OBJEXT): $(top_srcdir)/internal/imemo.h
hash.$(OBJEXT): $(top_srcdir)/internal/object.h
hash.$(OBJEXT): $(top_srcdir)/internal/proc.h
hash.$(OBJEXT): $(top_srcdir)/internal/serial.h
@@ -7039,9 +7223,11 @@ hash.$(OBJEXT): $(top_srcdir)/internal/string.h
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
hash.$(OBJEXT): $(top_srcdir)/internal/thread.h
hash.$(OBJEXT): $(top_srcdir)/internal/time.h
+hash.$(OBJEXT): $(top_srcdir)/internal/variable.h
hash.$(OBJEXT): $(top_srcdir)/internal/vm.h
hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h
hash.$(OBJEXT): {$(VPATH)}assert.h
+hash.$(OBJEXT): {$(VPATH)}atomic.h
hash.$(OBJEXT): {$(VPATH)}backward/2/assume.h
hash.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
hash.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -7052,6 +7238,7 @@ hash.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
hash.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
hash.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
hash.$(OBJEXT): {$(VPATH)}config.h
+hash.$(OBJEXT): {$(VPATH)}constant.h
hash.$(OBJEXT): {$(VPATH)}debug_counter.h
hash.$(OBJEXT): {$(VPATH)}defines.h
hash.$(OBJEXT): {$(VPATH)}encoding.h
@@ -7209,20 +7396,28 @@ hash.$(OBJEXT): {$(VPATH)}internal/value_type.h
hash.$(OBJEXT): {$(VPATH)}internal/variable.h
hash.$(OBJEXT): {$(VPATH)}internal/warning_push.h
hash.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+hash.$(OBJEXT): {$(VPATH)}iseq.h
+hash.$(OBJEXT): {$(VPATH)}method.h
hash.$(OBJEXT): {$(VPATH)}missing.h
+hash.$(OBJEXT): {$(VPATH)}node.h
hash.$(OBJEXT): {$(VPATH)}onigmo.h
hash.$(OBJEXT): {$(VPATH)}oniguruma.h
hash.$(OBJEXT): {$(VPATH)}probes.dmyh
hash.$(OBJEXT): {$(VPATH)}probes.h
hash.$(OBJEXT): {$(VPATH)}ractor.h
hash.$(OBJEXT): {$(VPATH)}ruby_assert.h
+hash.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+hash.$(OBJEXT): {$(VPATH)}shape.h
hash.$(OBJEXT): {$(VPATH)}st.h
hash.$(OBJEXT): {$(VPATH)}subst.h
hash.$(OBJEXT): {$(VPATH)}symbol.h
+hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
hash.$(OBJEXT): {$(VPATH)}thread_native.h
hash.$(OBJEXT): {$(VPATH)}transient_heap.h
hash.$(OBJEXT): {$(VPATH)}util.h
+hash.$(OBJEXT): {$(VPATH)}vm_core.h
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
+hash.$(OBJEXT): {$(VPATH)}vm_opts.h
hash.$(OBJEXT): {$(VPATH)}vm_sync.h
inits.$(OBJEXT): $(hdrdir)/ruby.h
inits.$(OBJEXT): $(hdrdir)/ruby/ruby.h
@@ -7394,6 +7589,7 @@ io.$(OBJEXT): $(CCAN_DIR)/list/list.h
io.$(OBJEXT): $(CCAN_DIR)/str/str.h
io.$(OBJEXT): $(hdrdir)/ruby/ruby.h
io.$(OBJEXT): $(top_srcdir)/internal/array.h
+io.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
io.$(OBJEXT): $(top_srcdir)/internal/bignum.h
io.$(OBJEXT): $(top_srcdir)/internal/bits.h
io.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -7600,6 +7796,7 @@ io.$(OBJEXT): {$(VPATH)}oniguruma.h
io.$(OBJEXT): {$(VPATH)}ractor.h
io.$(OBJEXT): {$(VPATH)}ruby_assert.h
io.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+io.$(OBJEXT): {$(VPATH)}shape.h
io.$(OBJEXT): {$(VPATH)}st.h
io.$(OBJEXT): {$(VPATH)}subst.h
io.$(OBJEXT): {$(VPATH)}thread.h
@@ -7609,11 +7806,18 @@ io.$(OBJEXT): {$(VPATH)}util.h
io.$(OBJEXT): {$(VPATH)}vm_core.h
io.$(OBJEXT): {$(VPATH)}vm_opts.h
io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/bignum.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/serial.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/string.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/thread.h
+io_buffer.$(OBJEXT): $(top_srcdir)/internal/vm.h
io_buffer.$(OBJEXT): {$(VPATH)}assert.h
io_buffer.$(OBJEXT): {$(VPATH)}backward/2/assume.h
io_buffer.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
@@ -7794,6 +7998,7 @@ iseq.$(OBJEXT): $(CCAN_DIR)/str/str.h
iseq.$(OBJEXT): $(hdrdir)/ruby.h
iseq.$(OBJEXT): $(hdrdir)/ruby/ruby.h
iseq.$(OBJEXT): $(top_srcdir)/internal/array.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
iseq.$(OBJEXT): $(top_srcdir)/internal/bits.h
iseq.$(OBJEXT): $(top_srcdir)/internal/class.h
iseq.$(OBJEXT): $(top_srcdir)/internal/compile.h
@@ -8000,6 +8205,7 @@ iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
iseq.$(OBJEXT): {$(VPATH)}ractor.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+iseq.$(OBJEXT): {$(VPATH)}shape.h
iseq.$(OBJEXT): {$(VPATH)}st.h
iseq.$(OBJEXT): {$(VPATH)}subst.h
iseq.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -8015,6 +8221,7 @@ load.$(OBJEXT): $(CCAN_DIR)/list/list.h
load.$(OBJEXT): $(CCAN_DIR)/str/str.h
load.$(OBJEXT): $(hdrdir)/ruby/ruby.h
load.$(OBJEXT): $(top_srcdir)/internal/array.h
+load.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
load.$(OBJEXT): $(top_srcdir)/internal/compilers.h
load.$(OBJEXT): $(top_srcdir)/internal/dir.h
load.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -8212,6 +8419,7 @@ load.$(OBJEXT): {$(VPATH)}probes.dmyh
load.$(OBJEXT): {$(VPATH)}probes.h
load.$(OBJEXT): {$(VPATH)}ruby_assert.h
load.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+load.$(OBJEXT): {$(VPATH)}shape.h
load.$(OBJEXT): {$(VPATH)}st.h
load.$(OBJEXT): {$(VPATH)}subst.h
load.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -8710,8 +8918,13 @@ main.$(OBJEXT): {$(VPATH)}missing.h
main.$(OBJEXT): {$(VPATH)}st.h
main.$(OBJEXT): {$(VPATH)}subst.h
main.$(OBJEXT): {$(VPATH)}vm_debug.h
+marshal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+marshal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+marshal.$(OBJEXT): $(CCAN_DIR)/list/list.h
+marshal.$(OBJEXT): $(CCAN_DIR)/str/str.h
marshal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
marshal.$(OBJEXT): $(top_srcdir)/internal/array.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
marshal.$(OBJEXT): $(top_srcdir)/internal/bignum.h
marshal.$(OBJEXT): $(top_srcdir)/internal/bits.h
marshal.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -8721,6 +8934,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/error.h
marshal.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
marshal.$(OBJEXT): $(top_srcdir)/internal/gc.h
marshal.$(OBJEXT): $(top_srcdir)/internal/hash.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/imemo.h
marshal.$(OBJEXT): $(top_srcdir)/internal/numeric.h
marshal.$(OBJEXT): $(top_srcdir)/internal/object.h
marshal.$(OBJEXT): $(top_srcdir)/internal/serial.h
@@ -8729,9 +8943,11 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/string.h
marshal.$(OBJEXT): $(top_srcdir)/internal/struct.h
marshal.$(OBJEXT): $(top_srcdir)/internal/symbol.h
marshal.$(OBJEXT): $(top_srcdir)/internal/util.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/variable.h
marshal.$(OBJEXT): $(top_srcdir)/internal/vm.h
marshal.$(OBJEXT): $(top_srcdir)/internal/warnings.h
marshal.$(OBJEXT): {$(VPATH)}assert.h
+marshal.$(OBJEXT): {$(VPATH)}atomic.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/assume.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -8743,9 +8959,11 @@ marshal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
marshal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
marshal.$(OBJEXT): {$(VPATH)}builtin.h
marshal.$(OBJEXT): {$(VPATH)}config.h
+marshal.$(OBJEXT): {$(VPATH)}constant.h
marshal.$(OBJEXT): {$(VPATH)}defines.h
marshal.$(OBJEXT): {$(VPATH)}encindex.h
marshal.$(OBJEXT): {$(VPATH)}encoding.h
+marshal.$(OBJEXT): {$(VPATH)}id.h
marshal.$(OBJEXT): {$(VPATH)}id_table.h
marshal.$(OBJEXT): {$(VPATH)}intern.h
marshal.$(OBJEXT): {$(VPATH)}internal.h
@@ -8786,6 +9004,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -8901,12 +9120,21 @@ marshal.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
marshal.$(OBJEXT): {$(VPATH)}io.h
marshal.$(OBJEXT): {$(VPATH)}marshal.c
marshal.$(OBJEXT): {$(VPATH)}marshal.rbinc
+marshal.$(OBJEXT): {$(VPATH)}method.h
marshal.$(OBJEXT): {$(VPATH)}missing.h
+marshal.$(OBJEXT): {$(VPATH)}node.h
marshal.$(OBJEXT): {$(VPATH)}onigmo.h
marshal.$(OBJEXT): {$(VPATH)}oniguruma.h
+marshal.$(OBJEXT): {$(VPATH)}ruby_assert.h
+marshal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+marshal.$(OBJEXT): {$(VPATH)}shape.h
marshal.$(OBJEXT): {$(VPATH)}st.h
marshal.$(OBJEXT): {$(VPATH)}subst.h
+marshal.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+marshal.$(OBJEXT): {$(VPATH)}thread_native.h
marshal.$(OBJEXT): {$(VPATH)}util.h
+marshal.$(OBJEXT): {$(VPATH)}vm_core.h
+marshal.$(OBJEXT): {$(VPATH)}vm_opts.h
math.$(OBJEXT): $(hdrdir)/ruby/ruby.h
math.$(OBJEXT): $(top_srcdir)/internal/bignum.h
math.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -8917,6 +9145,7 @@ math.$(OBJEXT): $(top_srcdir)/internal/math.h
math.$(OBJEXT): $(top_srcdir)/internal/object.h
math.$(OBJEXT): $(top_srcdir)/internal/serial.h
math.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+math.$(OBJEXT): $(top_srcdir)/internal/variable.h
math.$(OBJEXT): $(top_srcdir)/internal/vm.h
math.$(OBJEXT): $(top_srcdir)/internal/warnings.h
math.$(OBJEXT): {$(VPATH)}assert.h
@@ -8930,6 +9159,7 @@ math.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
math.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
math.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
math.$(OBJEXT): {$(VPATH)}config.h
+math.$(OBJEXT): {$(VPATH)}constant.h
math.$(OBJEXT): {$(VPATH)}defines.h
math.$(OBJEXT): {$(VPATH)}id_table.h
math.$(OBJEXT): {$(VPATH)}intern.h
@@ -9076,15 +9306,20 @@ math.$(OBJEXT): {$(VPATH)}internal/warning_push.h
math.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
math.$(OBJEXT): {$(VPATH)}math.c
math.$(OBJEXT): {$(VPATH)}missing.h
+math.$(OBJEXT): {$(VPATH)}shape.h
math.$(OBJEXT): {$(VPATH)}st.h
math.$(OBJEXT): {$(VPATH)}subst.h
memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/gc.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/hash.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/variable.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/warnings.h
memory_view.$(OBJEXT): {$(VPATH)}assert.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/assume.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+memory_view.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/limits.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
@@ -9240,6 +9475,7 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
memory_view.$(OBJEXT): {$(VPATH)}memory_view.c
memory_view.$(OBJEXT): {$(VPATH)}memory_view.h
memory_view.$(OBJEXT): {$(VPATH)}missing.h
+memory_view.$(OBJEXT): {$(VPATH)}shape.h
memory_view.$(OBJEXT): {$(VPATH)}st.h
memory_view.$(OBJEXT): {$(VPATH)}subst.h
memory_view.$(OBJEXT): {$(VPATH)}util.h
@@ -9250,12 +9486,15 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h
miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h
miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+miniinit.$(OBJEXT): $(srcdir)/mjit_c.rb
miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/serial.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
miniinit.$(OBJEXT): {$(VPATH)}array.rb
@@ -9273,12 +9512,14 @@ miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
miniinit.$(OBJEXT): {$(VPATH)}builtin.h
miniinit.$(OBJEXT): {$(VPATH)}config.h
+miniinit.$(OBJEXT): {$(VPATH)}constant.h
miniinit.$(OBJEXT): {$(VPATH)}defines.h
miniinit.$(OBJEXT): {$(VPATH)}dir.rb
miniinit.$(OBJEXT): {$(VPATH)}encoding.h
miniinit.$(OBJEXT): {$(VPATH)}gc.rb
miniinit.$(OBJEXT): {$(VPATH)}gem_prelude.rb
miniinit.$(OBJEXT): {$(VPATH)}id.h
+miniinit.$(OBJEXT): {$(VPATH)}id_table.h
miniinit.$(OBJEXT): {$(VPATH)}intern.h
miniinit.$(OBJEXT): {$(VPATH)}internal.h
miniinit.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -9318,6 +9559,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -9440,6 +9682,7 @@ miniinit.$(OBJEXT): {$(VPATH)}miniinit.c
miniinit.$(OBJEXT): {$(VPATH)}miniprelude.c
miniinit.$(OBJEXT): {$(VPATH)}missing.h
miniinit.$(OBJEXT): {$(VPATH)}mjit.rb
+miniinit.$(OBJEXT): {$(VPATH)}mjit_c.rb
miniinit.$(OBJEXT): {$(VPATH)}nilclass.rb
miniinit.$(OBJEXT): {$(VPATH)}node.h
miniinit.$(OBJEXT): {$(VPATH)}numeric.rb
@@ -9450,8 +9693,10 @@ miniinit.$(OBJEXT): {$(VPATH)}prelude.rb
miniinit.$(OBJEXT): {$(VPATH)}ractor.rb
miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h
miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+miniinit.$(OBJEXT): {$(VPATH)}shape.h
miniinit.$(OBJEXT): {$(VPATH)}st.h
miniinit.$(OBJEXT): {$(VPATH)}subst.h
+miniinit.$(OBJEXT): {$(VPATH)}symbol.rb
miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
miniinit.$(OBJEXT): {$(VPATH)}thread_native.h
miniinit.$(OBJEXT): {$(VPATH)}thread_sync.rb
@@ -9469,6 +9714,7 @@ mjit.$(OBJEXT): $(hdrdir)/ruby.h
mjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
mjit.$(OBJEXT): $(hdrdir)/ruby/version.h
mjit.$(OBJEXT): $(top_srcdir)/internal/array.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
mjit.$(OBJEXT): $(top_srcdir)/internal/class.h
mjit.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
mjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
@@ -9481,6 +9727,7 @@ mjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
mjit.$(OBJEXT): $(top_srcdir)/internal/process.h
mjit.$(OBJEXT): $(top_srcdir)/internal/serial.h
mjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
mjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
mjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
mjit.$(OBJEXT): {$(VPATH)}assert.h
@@ -9664,10 +9911,9 @@ mjit.$(OBJEXT): {$(VPATH)}method.h
mjit.$(OBJEXT): {$(VPATH)}missing.h
mjit.$(OBJEXT): {$(VPATH)}mjit.c
mjit.$(OBJEXT): {$(VPATH)}mjit.h
-mjit.$(OBJEXT): {$(VPATH)}mjit.rb
mjit.$(OBJEXT): {$(VPATH)}mjit.rbinc
+mjit.$(OBJEXT): {$(VPATH)}mjit_c.h
mjit.$(OBJEXT): {$(VPATH)}mjit_config.h
-mjit.$(OBJEXT): {$(VPATH)}mjit_unit.h
mjit.$(OBJEXT): {$(VPATH)}node.h
mjit.$(OBJEXT): {$(VPATH)}onigmo.h
mjit.$(OBJEXT): {$(VPATH)}oniguruma.h
@@ -9675,6 +9921,7 @@ mjit.$(OBJEXT): {$(VPATH)}ractor.h
mjit.$(OBJEXT): {$(VPATH)}ractor_core.h
mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
mjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+mjit.$(OBJEXT): {$(VPATH)}shape.h
mjit.$(OBJEXT): {$(VPATH)}st.h
mjit.$(OBJEXT): {$(VPATH)}subst.h
mjit.$(OBJEXT): {$(VPATH)}thread.h
@@ -9687,214 +9934,220 @@ mjit.$(OBJEXT): {$(VPATH)}vm_debug.h
mjit.$(OBJEXT): {$(VPATH)}vm_opts.h
mjit.$(OBJEXT): {$(VPATH)}vm_sync.h
mjit.$(OBJEXT): {$(VPATH)}yjit.h
-mjit_compiler.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
-mjit_compiler.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
-mjit_compiler.$(OBJEXT): $(CCAN_DIR)/list/list.h
-mjit_compiler.$(OBJEXT): $(CCAN_DIR)/str/str.h
-mjit_compiler.$(OBJEXT): $(hdrdir)/ruby.h
-mjit_compiler.$(OBJEXT): $(hdrdir)/ruby/ruby.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/array.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/class.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/compile.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/compilers.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/gc.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/hash.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/imemo.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/object.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/serial.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/variable.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/vm.h
-mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/warnings.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}assert.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}atomic.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/assume.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/bool.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/limits.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}builtin.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}config.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}constant.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}debug_counter.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}defines.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}id.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}id_table.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}insns.def
-mjit_compiler.$(OBJEXT): {$(VPATH)}insns.inc
-mjit_compiler.$(OBJEXT): {$(VPATH)}insns_info.inc
-mjit_compiler.$(OBJEXT): {$(VPATH)}intern.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/abi.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/anyargs.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/assume.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/const.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/error.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/format.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/cast.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/config.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/constant_p.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/robject.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/ctype.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/dllexport.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/dosish.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/error.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/eval.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/event.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/fl_type.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/gc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/glob.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/globals.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/extension.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/feature.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/has/warning.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/array.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/class.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/error.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/file.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/io.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/load.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/object.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/process.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/random.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/range.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/re.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/select.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/string.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/time.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/interpreter.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/iterator.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/memory.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/method.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/module.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/newobj.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/rgengc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/scan_args.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/special_consts.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/static_assert.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/stdalign.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/stdbool.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/symbol.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/value.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/value_type.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/variable.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/warning_push.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}iseq.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}method.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}missing.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}mjit.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compile.inc
-mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c
-mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}node.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_atomic.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}st.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}subst.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
-mjit_compiler.$(OBJEXT): {$(VPATH)}thread_native.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}vm_callinfo.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}vm_core.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}vm_exec.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}vm_opts.h
-mjit_compiler.$(OBJEXT): {$(VPATH)}yjit.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/list/list.h
+mjit_c.$(OBJEXT): $(CCAN_DIR)/str/str.h
+mjit_c.$(OBJEXT): $(hdrdir)/ruby.h
+mjit_c.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+mjit_c.$(OBJEXT): $(srcdir)/mjit_c.rb
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/array.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/class.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/compile.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/gc.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/hash.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/object.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/serial.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/variable.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/vm.h
+mjit_c.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+mjit_c.$(OBJEXT): {$(VPATH)}assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}atomic.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+mjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+mjit_c.$(OBJEXT): {$(VPATH)}builtin.h
+mjit_c.$(OBJEXT): {$(VPATH)}config.h
+mjit_c.$(OBJEXT): {$(VPATH)}constant.h
+mjit_c.$(OBJEXT): {$(VPATH)}debug_counter.h
+mjit_c.$(OBJEXT): {$(VPATH)}defines.h
+mjit_c.$(OBJEXT): {$(VPATH)}id.h
+mjit_c.$(OBJEXT): {$(VPATH)}id_table.h
+mjit_c.$(OBJEXT): {$(VPATH)}insns.def
+mjit_c.$(OBJEXT): {$(VPATH)}insns.inc
+mjit_c.$(OBJEXT): {$(VPATH)}insns_info.inc
+mjit_c.$(OBJEXT): {$(VPATH)}intern.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/abi.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/assume.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/cast.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/config.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/ctype.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/dosish.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/eval.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/event.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/gc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/glob.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/globals.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/iterator.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/memory.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/method.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/module.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/newobj.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/rgengc.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/symbol.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/value.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/value_type.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/variable.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+mjit_c.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+mjit_c.$(OBJEXT): {$(VPATH)}iseq.h
+mjit_c.$(OBJEXT): {$(VPATH)}method.h
+mjit_c.$(OBJEXT): {$(VPATH)}missing.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.c
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.h
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.rb
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_c.rbinc
+mjit_c.$(OBJEXT): {$(VPATH)}mjit_sp_inc.inc
+mjit_c.$(OBJEXT): {$(VPATH)}node.h
+mjit_c.$(OBJEXT): {$(VPATH)}ruby_assert.h
+mjit_c.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+mjit_c.$(OBJEXT): {$(VPATH)}shape.h
+mjit_c.$(OBJEXT): {$(VPATH)}st.h
+mjit_c.$(OBJEXT): {$(VPATH)}subst.h
+mjit_c.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+mjit_c.$(OBJEXT): {$(VPATH)}thread_native.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_callinfo.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_core.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_exec.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
+mjit_c.$(OBJEXT): {$(VPATH)}vm_opts.h
+mjit_c.$(OBJEXT): {$(VPATH)}yjit.h
node.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
node.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
node.$(OBJEXT): $(CCAN_DIR)/list/list.h
node.$(OBJEXT): $(CCAN_DIR)/str/str.h
node.$(OBJEXT): $(hdrdir)/ruby/ruby.h
node.$(OBJEXT): $(top_srcdir)/internal/array.h
+node.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
node.$(OBJEXT): $(top_srcdir)/internal/compilers.h
node.$(OBJEXT): $(top_srcdir)/internal/gc.h
node.$(OBJEXT): $(top_srcdir)/internal/hash.h
@@ -10068,6 +10321,7 @@ node.$(OBJEXT): {$(VPATH)}node.c
node.$(OBJEXT): {$(VPATH)}node.h
node.$(OBJEXT): {$(VPATH)}ruby_assert.h
node.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+node.$(OBJEXT): {$(VPATH)}shape.h
node.$(OBJEXT): {$(VPATH)}st.h
node.$(OBJEXT): {$(VPATH)}subst.h
node.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -10265,16 +10519,21 @@ numeric.$(OBJEXT): {$(VPATH)}internal/warning_push.h
numeric.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
numeric.$(OBJEXT): {$(VPATH)}missing.h
numeric.$(OBJEXT): {$(VPATH)}numeric.c
-numeric.$(OBJEXT): {$(VPATH)}numeric.rb
numeric.$(OBJEXT): {$(VPATH)}numeric.rbinc
numeric.$(OBJEXT): {$(VPATH)}onigmo.h
numeric.$(OBJEXT): {$(VPATH)}oniguruma.h
numeric.$(OBJEXT): {$(VPATH)}ruby_assert.h
+numeric.$(OBJEXT): {$(VPATH)}shape.h
numeric.$(OBJEXT): {$(VPATH)}st.h
numeric.$(OBJEXT): {$(VPATH)}subst.h
numeric.$(OBJEXT): {$(VPATH)}util.h
+object.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+object.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+object.$(OBJEXT): $(CCAN_DIR)/list/list.h
+object.$(OBJEXT): $(CCAN_DIR)/str/str.h
object.$(OBJEXT): $(hdrdir)/ruby/ruby.h
object.$(OBJEXT): $(top_srcdir)/internal/array.h
+object.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
object.$(OBJEXT): $(top_srcdir)/internal/bignum.h
object.$(OBJEXT): $(top_srcdir)/internal/bits.h
object.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -10283,6 +10542,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/error.h
object.$(OBJEXT): $(top_srcdir)/internal/eval.h
object.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
object.$(OBJEXT): $(top_srcdir)/internal/gc.h
+object.$(OBJEXT): $(top_srcdir)/internal/imemo.h
object.$(OBJEXT): $(top_srcdir)/internal/inits.h
object.$(OBJEXT): $(top_srcdir)/internal/numeric.h
object.$(OBJEXT): $(top_srcdir)/internal/object.h
@@ -10295,6 +10555,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/variable.h
object.$(OBJEXT): $(top_srcdir)/internal/vm.h
object.$(OBJEXT): $(top_srcdir)/internal/warnings.h
object.$(OBJEXT): {$(VPATH)}assert.h
+object.$(OBJEXT): {$(VPATH)}atomic.h
object.$(OBJEXT): {$(VPATH)}backward/2/assume.h
object.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
object.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -10462,22 +10723,32 @@ object.$(OBJEXT): {$(VPATH)}internal/value_type.h
object.$(OBJEXT): {$(VPATH)}internal/variable.h
object.$(OBJEXT): {$(VPATH)}internal/warning_push.h
object.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
-object.$(OBJEXT): {$(VPATH)}kernel.rb
object.$(OBJEXT): {$(VPATH)}kernel.rbinc
+object.$(OBJEXT): {$(VPATH)}method.h
object.$(OBJEXT): {$(VPATH)}missing.h
object.$(OBJEXT): {$(VPATH)}nilclass.rbinc
+object.$(OBJEXT): {$(VPATH)}node.h
object.$(OBJEXT): {$(VPATH)}object.c
object.$(OBJEXT): {$(VPATH)}onigmo.h
object.$(OBJEXT): {$(VPATH)}oniguruma.h
object.$(OBJEXT): {$(VPATH)}probes.dmyh
object.$(OBJEXT): {$(VPATH)}probes.h
+object.$(OBJEXT): {$(VPATH)}ruby_assert.h
+object.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+object.$(OBJEXT): {$(VPATH)}shape.h
object.$(OBJEXT): {$(VPATH)}st.h
object.$(OBJEXT): {$(VPATH)}subst.h
+object.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+object.$(OBJEXT): {$(VPATH)}thread_native.h
object.$(OBJEXT): {$(VPATH)}util.h
+object.$(OBJEXT): {$(VPATH)}variable.h
+object.$(OBJEXT): {$(VPATH)}vm_core.h
+object.$(OBJEXT): {$(VPATH)}vm_opts.h
pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h
pack.$(OBJEXT): $(top_srcdir)/internal/array.h
pack.$(OBJEXT): $(top_srcdir)/internal/bits.h
pack.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+pack.$(OBJEXT): $(top_srcdir)/internal/gc.h
pack.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
pack.$(OBJEXT): $(top_srcdir)/internal/string.h
pack.$(OBJEXT): $(top_srcdir)/internal/symbol.h
@@ -10655,6 +10926,7 @@ pack.$(OBJEXT): {$(VPATH)}onigmo.h
pack.$(OBJEXT): {$(VPATH)}oniguruma.h
pack.$(OBJEXT): {$(VPATH)}pack.c
pack.$(OBJEXT): {$(VPATH)}pack.rbinc
+pack.$(OBJEXT): {$(VPATH)}shape.h
pack.$(OBJEXT): {$(VPATH)}st.h
pack.$(OBJEXT): {$(VPATH)}subst.h
pack.$(OBJEXT): {$(VPATH)}util.h
@@ -10666,6 +10938,7 @@ parse.$(OBJEXT): $(top_srcdir)/internal/bits.h
parse.$(OBJEXT): $(top_srcdir)/internal/compile.h
parse.$(OBJEXT): $(top_srcdir)/internal/compilers.h
parse.$(OBJEXT): $(top_srcdir)/internal/complex.h
+parse.$(OBJEXT): $(top_srcdir)/internal/encoding.h
parse.$(OBJEXT): $(top_srcdir)/internal/error.h
parse.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
parse.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -10867,6 +11140,7 @@ parse.$(OBJEXT): {$(VPATH)}ractor.h
parse.$(OBJEXT): {$(VPATH)}regenc.h
parse.$(OBJEXT): {$(VPATH)}regex.h
parse.$(OBJEXT): {$(VPATH)}ruby_assert.h
+parse.$(OBJEXT): {$(VPATH)}shape.h
parse.$(OBJEXT): {$(VPATH)}st.h
parse.$(OBJEXT): {$(VPATH)}subst.h
parse.$(OBJEXT): {$(VPATH)}symbol.h
@@ -10877,11 +11151,13 @@ proc.$(OBJEXT): $(CCAN_DIR)/list/list.h
proc.$(OBJEXT): $(CCAN_DIR)/str/str.h
proc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
proc.$(OBJEXT): $(top_srcdir)/internal/array.h
+proc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
proc.$(OBJEXT): $(top_srcdir)/internal/class.h
proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h
proc.$(OBJEXT): $(top_srcdir)/internal/error.h
proc.$(OBJEXT): $(top_srcdir)/internal/eval.h
proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
+proc.$(OBJEXT): $(top_srcdir)/internal/hash.h
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h
@@ -10889,6 +11165,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/serial.h
proc.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
proc.$(OBJEXT): $(top_srcdir)/internal/string.h
proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+proc.$(OBJEXT): $(top_srcdir)/internal/variable.h
proc.$(OBJEXT): $(top_srcdir)/internal/vm.h
proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h
proc.$(OBJEXT): {$(VPATH)}assert.h
@@ -10903,6 +11180,7 @@ proc.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
proc.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
proc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
proc.$(OBJEXT): {$(VPATH)}config.h
+proc.$(OBJEXT): {$(VPATH)}constant.h
proc.$(OBJEXT): {$(VPATH)}defines.h
proc.$(OBJEXT): {$(VPATH)}encoding.h
proc.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -11069,6 +11347,7 @@ proc.$(OBJEXT): {$(VPATH)}oniguruma.h
proc.$(OBJEXT): {$(VPATH)}proc.c
proc.$(OBJEXT): {$(VPATH)}ruby_assert.h
proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+proc.$(OBJEXT): {$(VPATH)}shape.h
proc.$(OBJEXT): {$(VPATH)}st.h
proc.$(OBJEXT): {$(VPATH)}subst.h
proc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -11083,6 +11362,7 @@ process.$(OBJEXT): $(CCAN_DIR)/str/str.h
process.$(OBJEXT): $(hdrdir)/ruby.h
process.$(OBJEXT): $(hdrdir)/ruby/ruby.h
process.$(OBJEXT): $(top_srcdir)/internal/array.h
+process.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
process.$(OBJEXT): $(top_srcdir)/internal/bignum.h
process.$(OBJEXT): $(top_srcdir)/internal/bits.h
process.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -11288,6 +11568,7 @@ process.$(OBJEXT): {$(VPATH)}process.c
process.$(OBJEXT): {$(VPATH)}ractor.h
process.$(OBJEXT): {$(VPATH)}ruby_assert.h
process.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+process.$(OBJEXT): {$(VPATH)}shape.h
process.$(OBJEXT): {$(VPATH)}st.h
process.$(OBJEXT): {$(VPATH)}subst.h
process.$(OBJEXT): {$(VPATH)}thread.h
@@ -11300,8 +11581,10 @@ ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h
ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h
+ractor.$(OBJEXT): $(hdrdir)/ruby.h
ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ractor.$(OBJEXT): $(top_srcdir)/internal/array.h
+ractor.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ractor.$(OBJEXT): $(top_srcdir)/internal/bignum.h
ractor.$(OBJEXT): $(top_srcdir)/internal/bits.h
ractor.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -11318,6 +11601,7 @@ ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ractor.$(OBJEXT): $(top_srcdir)/internal/string.h
ractor.$(OBJEXT): $(top_srcdir)/internal/struct.h
ractor.$(OBJEXT): $(top_srcdir)/internal/thread.h
+ractor.$(OBJEXT): $(top_srcdir)/internal/variable.h
ractor.$(OBJEXT): $(top_srcdir)/internal/vm.h
ractor.$(OBJEXT): $(top_srcdir)/internal/warnings.h
ractor.$(OBJEXT): {$(VPATH)}assert.h
@@ -11333,6 +11617,7 @@ ractor.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
ractor.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
ractor.$(OBJEXT): {$(VPATH)}builtin.h
ractor.$(OBJEXT): {$(VPATH)}config.h
+ractor.$(OBJEXT): {$(VPATH)}constant.h
ractor.$(OBJEXT): {$(VPATH)}debug_counter.h
ractor.$(OBJEXT): {$(VPATH)}defines.h
ractor.$(OBJEXT): {$(VPATH)}encoding.h
@@ -11492,16 +11777,17 @@ ractor.$(OBJEXT): {$(VPATH)}internal/warning_push.h
ractor.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
ractor.$(OBJEXT): {$(VPATH)}method.h
ractor.$(OBJEXT): {$(VPATH)}missing.h
+ractor.$(OBJEXT): {$(VPATH)}mjit.h
ractor.$(OBJEXT): {$(VPATH)}node.h
ractor.$(OBJEXT): {$(VPATH)}onigmo.h
ractor.$(OBJEXT): {$(VPATH)}oniguruma.h
ractor.$(OBJEXT): {$(VPATH)}ractor.c
ractor.$(OBJEXT): {$(VPATH)}ractor.h
-ractor.$(OBJEXT): {$(VPATH)}ractor.rb
ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc
ractor.$(OBJEXT): {$(VPATH)}ractor_core.h
ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h
ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ractor.$(OBJEXT): {$(VPATH)}shape.h
ractor.$(OBJEXT): {$(VPATH)}st.h
ractor.$(OBJEXT): {$(VPATH)}subst.h
ractor.$(OBJEXT): {$(VPATH)}thread.h
@@ -11520,6 +11806,7 @@ random.$(OBJEXT): $(top_srcdir)/internal/bignum.h
random.$(OBJEXT): $(top_srcdir)/internal/bits.h
random.$(OBJEXT): $(top_srcdir)/internal/compilers.h
random.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+random.$(OBJEXT): $(top_srcdir)/internal/gc.h
random.$(OBJEXT): $(top_srcdir)/internal/numeric.h
random.$(OBJEXT): $(top_srcdir)/internal/random.h
random.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
@@ -11691,12 +11978,14 @@ random.$(OBJEXT): {$(VPATH)}ractor.h
random.$(OBJEXT): {$(VPATH)}random.c
random.$(OBJEXT): {$(VPATH)}random.h
random.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+random.$(OBJEXT): {$(VPATH)}shape.h
random.$(OBJEXT): {$(VPATH)}siphash.c
random.$(OBJEXT): {$(VPATH)}siphash.h
random.$(OBJEXT): {$(VPATH)}st.h
random.$(OBJEXT): {$(VPATH)}subst.h
range.$(OBJEXT): $(hdrdir)/ruby/ruby.h
range.$(OBJEXT): $(top_srcdir)/internal/array.h
+range.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
range.$(OBJEXT): $(top_srcdir)/internal/bignum.h
range.$(OBJEXT): $(top_srcdir)/internal/bits.h
range.$(OBJEXT): $(top_srcdir)/internal/compar.h
@@ -11883,6 +12172,7 @@ range.$(OBJEXT): {$(VPATH)}missing.h
range.$(OBJEXT): {$(VPATH)}onigmo.h
range.$(OBJEXT): {$(VPATH)}oniguruma.h
range.$(OBJEXT): {$(VPATH)}range.c
+range.$(OBJEXT): {$(VPATH)}shape.h
range.$(OBJEXT): {$(VPATH)}st.h
range.$(OBJEXT): {$(VPATH)}subst.h
rational.$(OBJEXT): $(hdrdir)/ruby/ruby.h
@@ -11899,6 +12189,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/object.h
rational.$(OBJEXT): $(top_srcdir)/internal/rational.h
rational.$(OBJEXT): $(top_srcdir)/internal/serial.h
rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+rational.$(OBJEXT): $(top_srcdir)/internal/variable.h
rational.$(OBJEXT): $(top_srcdir)/internal/vm.h
rational.$(OBJEXT): $(top_srcdir)/internal/warnings.h
rational.$(OBJEXT): {$(VPATH)}assert.h
@@ -11912,6 +12203,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
rational.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rational.$(OBJEXT): {$(VPATH)}config.h
+rational.$(OBJEXT): {$(VPATH)}constant.h
rational.$(OBJEXT): {$(VPATH)}defines.h
rational.$(OBJEXT): {$(VPATH)}id.h
rational.$(OBJEXT): {$(VPATH)}id_table.h
@@ -12060,6 +12352,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
rational.$(OBJEXT): {$(VPATH)}missing.h
rational.$(OBJEXT): {$(VPATH)}rational.c
rational.$(OBJEXT): {$(VPATH)}ruby_assert.h
+rational.$(OBJEXT): {$(VPATH)}shape.h
rational.$(OBJEXT): {$(VPATH)}st.h
rational.$(OBJEXT): {$(VPATH)}subst.h
re.$(OBJEXT): $(hdrdir)/ruby.h
@@ -12068,6 +12361,7 @@ re.$(OBJEXT): $(top_srcdir)/internal/array.h
re.$(OBJEXT): $(top_srcdir)/internal/bits.h
re.$(OBJEXT): $(top_srcdir)/internal/class.h
re.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+re.$(OBJEXT): $(top_srcdir)/internal/encoding.h
re.$(OBJEXT): $(top_srcdir)/internal/gc.h
re.$(OBJEXT): $(top_srcdir)/internal/hash.h
re.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -12257,6 +12551,7 @@ re.$(OBJEXT): {$(VPATH)}re.h
re.$(OBJEXT): {$(VPATH)}regenc.h
re.$(OBJEXT): {$(VPATH)}regex.h
re.$(OBJEXT): {$(VPATH)}regint.h
+re.$(OBJEXT): {$(VPATH)}shape.h
re.$(OBJEXT): {$(VPATH)}st.h
re.$(OBJEXT): {$(VPATH)}subst.h
re.$(OBJEXT): {$(VPATH)}util.h
@@ -13252,9 +13547,11 @@ ruby.$(OBJEXT): $(hdrdir)/ruby.h
ruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ruby.$(OBJEXT): $(hdrdir)/ruby/version.h
ruby.$(OBJEXT): $(top_srcdir)/internal/array.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ruby.$(OBJEXT): $(top_srcdir)/internal/class.h
ruby.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
ruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/cont.h
ruby.$(OBJEXT): $(top_srcdir)/internal/error.h
ruby.$(OBJEXT): $(top_srcdir)/internal/file.h
ruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -13445,6 +13742,7 @@ ruby.$(OBJEXT): {$(VPATH)}internal/variable.h
ruby.$(OBJEXT): {$(VPATH)}internal/warning_push.h
ruby.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
ruby.$(OBJEXT): {$(VPATH)}io.h
+ruby.$(OBJEXT): {$(VPATH)}iseq.h
ruby.$(OBJEXT): {$(VPATH)}method.h
ruby.$(OBJEXT): {$(VPATH)}missing.h
ruby.$(OBJEXT): {$(VPATH)}mjit.h
@@ -13454,6 +13752,7 @@ ruby.$(OBJEXT): {$(VPATH)}oniguruma.h
ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ruby.$(OBJEXT): {$(VPATH)}shape.h
ruby.$(OBJEXT): {$(VPATH)}st.h
ruby.$(OBJEXT): {$(VPATH)}subst.h
ruby.$(OBJEXT): {$(VPATH)}thread.h
@@ -13469,12 +13768,14 @@ scheduler.$(OBJEXT): $(CCAN_DIR)/list/list.h
scheduler.$(OBJEXT): $(CCAN_DIR)/str/str.h
scheduler.$(OBJEXT): $(hdrdir)/ruby/ruby.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/array.h
+scheduler.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/compilers.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/gc.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/imemo.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/serial.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/thread.h
+scheduler.$(OBJEXT): $(top_srcdir)/internal/variable.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/vm.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/warnings.h
scheduler.$(OBJEXT): {$(VPATH)}assert.h
@@ -13489,10 +13790,12 @@ scheduler.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
scheduler.$(OBJEXT): {$(VPATH)}config.h
+scheduler.$(OBJEXT): {$(VPATH)}constant.h
scheduler.$(OBJEXT): {$(VPATH)}defines.h
scheduler.$(OBJEXT): {$(VPATH)}encoding.h
scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
scheduler.$(OBJEXT): {$(VPATH)}id.h
+scheduler.$(OBJEXT): {$(VPATH)}id_table.h
scheduler.$(OBJEXT): {$(VPATH)}intern.h
scheduler.$(OBJEXT): {$(VPATH)}internal.h
scheduler.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -13654,6 +13957,7 @@ scheduler.$(OBJEXT): {$(VPATH)}oniguruma.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_assert.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_atomic.h
scheduler.$(OBJEXT): {$(VPATH)}scheduler.c
+scheduler.$(OBJEXT): {$(VPATH)}shape.h
scheduler.$(OBJEXT): {$(VPATH)}st.h
scheduler.$(OBJEXT): {$(VPATH)}subst.h
scheduler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -13819,12 +14123,219 @@ setproctitle.$(OBJEXT): {$(VPATH)}setproctitle.c
setproctitle.$(OBJEXT): {$(VPATH)}st.h
setproctitle.$(OBJEXT): {$(VPATH)}subst.h
setproctitle.$(OBJEXT): {$(VPATH)}util.h
+shape.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+shape.$(OBJEXT): $(CCAN_DIR)/list/list.h
+shape.$(OBJEXT): $(CCAN_DIR)/str/str.h
+shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+shape.$(OBJEXT): $(top_srcdir)/internal/array.h
+shape.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+shape.$(OBJEXT): $(top_srcdir)/internal/class.h
+shape.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+shape.$(OBJEXT): $(top_srcdir)/internal/gc.h
+shape.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
+shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+shape.$(OBJEXT): $(top_srcdir)/internal/variable.h
+shape.$(OBJEXT): $(top_srcdir)/internal/vm.h
+shape.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+shape.$(OBJEXT): {$(VPATH)}assert.h
+shape.$(OBJEXT): {$(VPATH)}atomic.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+shape.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+shape.$(OBJEXT): {$(VPATH)}config.h
+shape.$(OBJEXT): {$(VPATH)}constant.h
+shape.$(OBJEXT): {$(VPATH)}debug_counter.h
+shape.$(OBJEXT): {$(VPATH)}defines.h
+shape.$(OBJEXT): {$(VPATH)}encoding.h
+shape.$(OBJEXT): {$(VPATH)}gc.h
+shape.$(OBJEXT): {$(VPATH)}id.h
+shape.$(OBJEXT): {$(VPATH)}id_table.h
+shape.$(OBJEXT): {$(VPATH)}intern.h
+shape.$(OBJEXT): {$(VPATH)}internal.h
+shape.$(OBJEXT): {$(VPATH)}internal/abi.h
+shape.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+shape.$(OBJEXT): {$(VPATH)}internal/assume.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+shape.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+shape.$(OBJEXT): {$(VPATH)}internal/cast.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+shape.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+shape.$(OBJEXT): {$(VPATH)}internal/config.h
+shape.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+shape.$(OBJEXT): {$(VPATH)}internal/core.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+shape.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+shape.$(OBJEXT): {$(VPATH)}internal/ctype.h
+shape.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+shape.$(OBJEXT): {$(VPATH)}internal/dosish.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+shape.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+shape.$(OBJEXT): {$(VPATH)}internal/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/eval.h
+shape.$(OBJEXT): {$(VPATH)}internal/event.h
+shape.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+shape.$(OBJEXT): {$(VPATH)}internal/gc.h
+shape.$(OBJEXT): {$(VPATH)}internal/glob.h
+shape.$(OBJEXT): {$(VPATH)}internal/globals.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+shape.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+shape.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+shape.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+shape.$(OBJEXT): {$(VPATH)}internal/iterator.h
+shape.$(OBJEXT): {$(VPATH)}internal/memory.h
+shape.$(OBJEXT): {$(VPATH)}internal/method.h
+shape.$(OBJEXT): {$(VPATH)}internal/module.h
+shape.$(OBJEXT): {$(VPATH)}internal/newobj.h
+shape.$(OBJEXT): {$(VPATH)}internal/rgengc.h
+shape.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+shape.$(OBJEXT): {$(VPATH)}internal/symbol.h
+shape.$(OBJEXT): {$(VPATH)}internal/value.h
+shape.$(OBJEXT): {$(VPATH)}internal/value_type.h
+shape.$(OBJEXT): {$(VPATH)}internal/variable.h
+shape.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+shape.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+shape.$(OBJEXT): {$(VPATH)}method.h
+shape.$(OBJEXT): {$(VPATH)}missing.h
+shape.$(OBJEXT): {$(VPATH)}node.h
+shape.$(OBJEXT): {$(VPATH)}onigmo.h
+shape.$(OBJEXT): {$(VPATH)}oniguruma.h
+shape.$(OBJEXT): {$(VPATH)}ruby_assert.h
+shape.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+shape.$(OBJEXT): {$(VPATH)}shape.c
+shape.$(OBJEXT): {$(VPATH)}shape.h
+shape.$(OBJEXT): {$(VPATH)}st.h
+shape.$(OBJEXT): {$(VPATH)}subst.h
+shape.$(OBJEXT): {$(VPATH)}symbol.h
+shape.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+shape.$(OBJEXT): {$(VPATH)}thread_native.h
+shape.$(OBJEXT): {$(VPATH)}variable.h
+shape.$(OBJEXT): {$(VPATH)}vm_core.h
+shape.$(OBJEXT): {$(VPATH)}vm_debug.h
+shape.$(OBJEXT): {$(VPATH)}vm_opts.h
+shape.$(OBJEXT): {$(VPATH)}vm_sync.h
signal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
signal.$(OBJEXT): $(CCAN_DIR)/list/list.h
signal.$(OBJEXT): $(CCAN_DIR)/str/str.h
signal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
signal.$(OBJEXT): $(top_srcdir)/internal/array.h
+signal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
signal.$(OBJEXT): $(top_srcdir)/internal/compilers.h
signal.$(OBJEXT): $(top_srcdir)/internal/eval.h
signal.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -13835,6 +14346,7 @@ signal.$(OBJEXT): $(top_srcdir)/internal/signal.h
signal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
signal.$(OBJEXT): $(top_srcdir)/internal/string.h
signal.$(OBJEXT): $(top_srcdir)/internal/thread.h
+signal.$(OBJEXT): $(top_srcdir)/internal/variable.h
signal.$(OBJEXT): $(top_srcdir)/internal/vm.h
signal.$(OBJEXT): $(top_srcdir)/internal/warnings.h
signal.$(OBJEXT): {$(VPATH)}assert.h
@@ -13849,6 +14361,7 @@ signal.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
signal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
signal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
signal.$(OBJEXT): {$(VPATH)}config.h
+signal.$(OBJEXT): {$(VPATH)}constant.h
signal.$(OBJEXT): {$(VPATH)}debug_counter.h
signal.$(OBJEXT): {$(VPATH)}defines.h
signal.$(OBJEXT): {$(VPATH)}encoding.h
@@ -13894,6 +14407,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -14015,6 +14529,7 @@ signal.$(OBJEXT): {$(VPATH)}ractor.h
signal.$(OBJEXT): {$(VPATH)}ractor_core.h
signal.$(OBJEXT): {$(VPATH)}ruby_assert.h
signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+signal.$(OBJEXT): {$(VPATH)}shape.h
signal.$(OBJEXT): {$(VPATH)}signal.c
signal.$(OBJEXT): {$(VPATH)}st.h
signal.$(OBJEXT): {$(VPATH)}subst.h
@@ -14039,6 +14554,7 @@ sprintf.$(OBJEXT): $(top_srcdir)/internal/serial.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/string.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+sprintf.$(OBJEXT): $(top_srcdir)/internal/variable.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/vm.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/warnings.h
sprintf.$(OBJEXT): {$(VPATH)}assert.h
@@ -14052,6 +14568,7 @@ sprintf.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
sprintf.$(OBJEXT): {$(VPATH)}config.h
+sprintf.$(OBJEXT): {$(VPATH)}constant.h
sprintf.$(OBJEXT): {$(VPATH)}defines.h
sprintf.$(OBJEXT): {$(VPATH)}encoding.h
sprintf.$(OBJEXT): {$(VPATH)}id.h
@@ -14213,6 +14730,7 @@ sprintf.$(OBJEXT): {$(VPATH)}onigmo.h
sprintf.$(OBJEXT): {$(VPATH)}oniguruma.h
sprintf.$(OBJEXT): {$(VPATH)}re.h
sprintf.$(OBJEXT): {$(VPATH)}regex.h
+sprintf.$(OBJEXT): {$(VPATH)}shape.h
sprintf.$(OBJEXT): {$(VPATH)}sprintf.c
sprintf.$(OBJEXT): {$(VPATH)}st.h
sprintf.$(OBJEXT): {$(VPATH)}subst.h
@@ -14380,11 +14898,13 @@ st.$(OBJEXT): {$(VPATH)}internal/variable.h
st.$(OBJEXT): {$(VPATH)}internal/warning_push.h
st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
st.$(OBJEXT): {$(VPATH)}missing.h
+st.$(OBJEXT): {$(VPATH)}ruby_assert.h
st.$(OBJEXT): {$(VPATH)}st.c
st.$(OBJEXT): {$(VPATH)}st.h
st.$(OBJEXT): {$(VPATH)}subst.h
strftime.$(OBJEXT): $(hdrdir)/ruby/ruby.h
strftime.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+strftime.$(OBJEXT): $(top_srcdir)/internal/encoding.h
strftime.$(OBJEXT): $(top_srcdir)/internal/serial.h
strftime.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
strftime.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -14563,6 +15083,7 @@ strftime.$(OBJEXT): {$(VPATH)}timev.h
strftime.$(OBJEXT): {$(VPATH)}util.h
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
string.$(OBJEXT): $(top_srcdir)/internal/array.h
+string.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
string.$(OBJEXT): $(top_srcdir)/internal/bignum.h
string.$(OBJEXT): $(top_srcdir)/internal/bits.h
string.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -14581,6 +15102,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h
string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
string.$(OBJEXT): $(top_srcdir)/internal/string.h
string.$(OBJEXT): $(top_srcdir)/internal/transcode.h
+string.$(OBJEXT): $(top_srcdir)/internal/variable.h
string.$(OBJEXT): $(top_srcdir)/internal/vm.h
string.$(OBJEXT): $(top_srcdir)/internal/warnings.h
string.$(OBJEXT): {$(VPATH)}assert.h
@@ -14595,6 +15117,7 @@ string.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
string.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
string.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
string.$(OBJEXT): {$(VPATH)}config.h
+string.$(OBJEXT): {$(VPATH)}constant.h
string.$(OBJEXT): {$(VPATH)}debug_counter.h
string.$(OBJEXT): {$(VPATH)}defines.h
string.$(OBJEXT): {$(VPATH)}encindex.h
@@ -14641,6 +15164,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
string.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -14762,6 +15286,7 @@ string.$(OBJEXT): {$(VPATH)}probes.h
string.$(OBJEXT): {$(VPATH)}re.h
string.$(OBJEXT): {$(VPATH)}regex.h
string.$(OBJEXT): {$(VPATH)}ruby_assert.h
+string.$(OBJEXT): {$(VPATH)}shape.h
string.$(OBJEXT): {$(VPATH)}st.h
string.$(OBJEXT): {$(VPATH)}string.c
string.$(OBJEXT): {$(VPATH)}subst.h
@@ -14805,6 +15330,7 @@ struct.$(OBJEXT): $(CCAN_DIR)/list/list.h
struct.$(OBJEXT): $(CCAN_DIR)/str/str.h
struct.$(OBJEXT): $(hdrdir)/ruby/ruby.h
struct.$(OBJEXT): $(top_srcdir)/internal/array.h
+struct.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
struct.$(OBJEXT): $(top_srcdir)/internal/class.h
struct.$(OBJEXT): $(top_srcdir)/internal/compilers.h
struct.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -14818,6 +15344,7 @@ struct.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
struct.$(OBJEXT): $(top_srcdir)/internal/string.h
struct.$(OBJEXT): $(top_srcdir)/internal/struct.h
struct.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+struct.$(OBJEXT): $(top_srcdir)/internal/variable.h
struct.$(OBJEXT): $(top_srcdir)/internal/vm.h
struct.$(OBJEXT): $(top_srcdir)/internal/warnings.h
struct.$(OBJEXT): {$(VPATH)}assert.h
@@ -14833,6 +15360,7 @@ struct.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
struct.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
struct.$(OBJEXT): {$(VPATH)}builtin.h
struct.$(OBJEXT): {$(VPATH)}config.h
+struct.$(OBJEXT): {$(VPATH)}constant.h
struct.$(OBJEXT): {$(VPATH)}defines.h
struct.$(OBJEXT): {$(VPATH)}encoding.h
struct.$(OBJEXT): {$(VPATH)}id.h
@@ -14995,6 +15523,7 @@ struct.$(OBJEXT): {$(VPATH)}onigmo.h
struct.$(OBJEXT): {$(VPATH)}oniguruma.h
struct.$(OBJEXT): {$(VPATH)}ruby_assert.h
struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+struct.$(OBJEXT): {$(VPATH)}shape.h
struct.$(OBJEXT): {$(VPATH)}st.h
struct.$(OBJEXT): {$(VPATH)}struct.c
struct.$(OBJEXT): {$(VPATH)}subst.h
@@ -15014,6 +15543,7 @@ symbol.$(OBJEXT): $(top_srcdir)/internal/serial.h
symbol.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
symbol.$(OBJEXT): $(top_srcdir)/internal/string.h
symbol.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+symbol.$(OBJEXT): $(top_srcdir)/internal/variable.h
symbol.$(OBJEXT): $(top_srcdir)/internal/vm.h
symbol.$(OBJEXT): $(top_srcdir)/internal/warnings.h
symbol.$(OBJEXT): {$(VPATH)}assert.h
@@ -15026,7 +15556,9 @@ symbol.$(OBJEXT): {$(VPATH)}backward/2/limits.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
symbol.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+symbol.$(OBJEXT): {$(VPATH)}builtin.h
symbol.$(OBJEXT): {$(VPATH)}config.h
+symbol.$(OBJEXT): {$(VPATH)}constant.h
symbol.$(OBJEXT): {$(VPATH)}debug_counter.h
symbol.$(OBJEXT): {$(VPATH)}defines.h
symbol.$(OBJEXT): {$(VPATH)}encoding.h
@@ -15074,6 +15606,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
@@ -15192,10 +15725,13 @@ symbol.$(OBJEXT): {$(VPATH)}oniguruma.h
symbol.$(OBJEXT): {$(VPATH)}probes.dmyh
symbol.$(OBJEXT): {$(VPATH)}probes.h
symbol.$(OBJEXT): {$(VPATH)}ruby_assert.h
+symbol.$(OBJEXT): {$(VPATH)}shape.h
symbol.$(OBJEXT): {$(VPATH)}st.h
symbol.$(OBJEXT): {$(VPATH)}subst.h
symbol.$(OBJEXT): {$(VPATH)}symbol.c
symbol.$(OBJEXT): {$(VPATH)}symbol.h
+symbol.$(OBJEXT): {$(VPATH)}symbol.rb
+symbol.$(OBJEXT): {$(VPATH)}symbol.rbinc
symbol.$(OBJEXT): {$(VPATH)}vm_debug.h
symbol.$(OBJEXT): {$(VPATH)}vm_sync.h
thread.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
@@ -15205,6 +15741,7 @@ thread.$(OBJEXT): $(CCAN_DIR)/str/str.h
thread.$(OBJEXT): $(hdrdir)/ruby.h
thread.$(OBJEXT): $(hdrdir)/ruby/ruby.h
thread.$(OBJEXT): $(top_srcdir)/internal/array.h
+thread.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
thread.$(OBJEXT): $(top_srcdir)/internal/bits.h
thread.$(OBJEXT): $(top_srcdir)/internal/class.h
thread.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -15222,6 +15759,7 @@ thread.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
thread.$(OBJEXT): $(top_srcdir)/internal/string.h
thread.$(OBJEXT): $(top_srcdir)/internal/thread.h
thread.$(OBJEXT): $(top_srcdir)/internal/time.h
+thread.$(OBJEXT): $(top_srcdir)/internal/variable.h
thread.$(OBJEXT): $(top_srcdir)/internal/vm.h
thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h
thread.$(OBJEXT): {$(VPATH)}assert.h
@@ -15237,6 +15775,7 @@ thread.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
thread.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
thread.$(OBJEXT): {$(VPATH)}builtin.h
thread.$(OBJEXT): {$(VPATH)}config.h
+thread.$(OBJEXT): {$(VPATH)}constant.h
thread.$(OBJEXT): {$(VPATH)}debug.h
thread.$(OBJEXT): {$(VPATH)}debug_counter.h
thread.$(OBJEXT): {$(VPATH)}defines.h
@@ -15410,6 +15949,7 @@ thread.$(OBJEXT): {$(VPATH)}ractor.h
thread.$(OBJEXT): {$(VPATH)}ractor_core.h
thread.$(OBJEXT): {$(VPATH)}ruby_assert.h
thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+thread.$(OBJEXT): {$(VPATH)}shape.h
thread.$(OBJEXT): {$(VPATH)}st.h
thread.$(OBJEXT): {$(VPATH)}subst.h
thread.$(OBJEXT): {$(VPATH)}thread.c
@@ -15418,7 +15958,6 @@ thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).c
thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
thread.$(OBJEXT): {$(VPATH)}thread_native.h
thread.$(OBJEXT): {$(VPATH)}thread_sync.c
-thread.$(OBJEXT): {$(VPATH)}thread_sync.rb
thread.$(OBJEXT): {$(VPATH)}thread_sync.rbinc
thread.$(OBJEXT): {$(VPATH)}timev.h
thread.$(OBJEXT): {$(VPATH)}vm_core.h
@@ -15427,12 +15966,14 @@ thread.$(OBJEXT): {$(VPATH)}vm_opts.h
thread.$(OBJEXT): {$(VPATH)}vm_sync.h
time.$(OBJEXT): $(hdrdir)/ruby/ruby.h
time.$(OBJEXT): $(top_srcdir)/internal/array.h
+time.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
time.$(OBJEXT): $(top_srcdir)/internal/bignum.h
time.$(OBJEXT): $(top_srcdir)/internal/bits.h
time.$(OBJEXT): $(top_srcdir)/internal/compar.h
time.$(OBJEXT): $(top_srcdir)/internal/compilers.h
time.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
time.$(OBJEXT): $(top_srcdir)/internal/gc.h
+time.$(OBJEXT): $(top_srcdir)/internal/hash.h
time.$(OBJEXT): $(top_srcdir)/internal/numeric.h
time.$(OBJEXT): $(top_srcdir)/internal/rational.h
time.$(OBJEXT): $(top_srcdir)/internal/serial.h
@@ -15614,6 +16155,7 @@ time.$(OBJEXT): {$(VPATH)}missing.h
time.$(OBJEXT): {$(VPATH)}onigmo.h
time.$(OBJEXT): {$(VPATH)}oniguruma.h
time.$(OBJEXT): {$(VPATH)}ruby_assert.h
+time.$(OBJEXT): {$(VPATH)}shape.h
time.$(OBJEXT): {$(VPATH)}st.h
time.$(OBJEXT): {$(VPATH)}subst.h
time.$(OBJEXT): {$(VPATH)}time.c
@@ -15630,6 +16172,7 @@ transcode.$(OBJEXT): $(top_srcdir)/internal/serial.h
transcode.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
transcode.$(OBJEXT): $(top_srcdir)/internal/string.h
transcode.$(OBJEXT): $(top_srcdir)/internal/transcode.h
+transcode.$(OBJEXT): $(top_srcdir)/internal/variable.h
transcode.$(OBJEXT): $(top_srcdir)/internal/warnings.h
transcode.$(OBJEXT): {$(VPATH)}assert.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -15642,6 +16185,7 @@ transcode.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
transcode.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
transcode.$(OBJEXT): {$(VPATH)}config.h
+transcode.$(OBJEXT): {$(VPATH)}constant.h
transcode.$(OBJEXT): {$(VPATH)}defines.h
transcode.$(OBJEXT): {$(VPATH)}encoding.h
transcode.$(OBJEXT): {$(VPATH)}id.h
@@ -15800,6 +16344,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
transcode.$(OBJEXT): {$(VPATH)}missing.h
transcode.$(OBJEXT): {$(VPATH)}onigmo.h
transcode.$(OBJEXT): {$(VPATH)}oniguruma.h
+transcode.$(OBJEXT): {$(VPATH)}shape.h
transcode.$(OBJEXT): {$(VPATH)}st.h
transcode.$(OBJEXT): {$(VPATH)}subst.h
transcode.$(OBJEXT): {$(VPATH)}transcode.c
@@ -15975,6 +16520,7 @@ transient_heap.$(OBJEXT): {$(VPATH)}internal/warning_push.h
transient_heap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
transient_heap.$(OBJEXT): {$(VPATH)}missing.h
transient_heap.$(OBJEXT): {$(VPATH)}ruby_assert.h
+transient_heap.$(OBJEXT): {$(VPATH)}shape.h
transient_heap.$(OBJEXT): {$(VPATH)}st.h
transient_heap.$(OBJEXT): {$(VPATH)}subst.h
transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c
@@ -16154,6 +16700,7 @@ variable.$(OBJEXT): $(CCAN_DIR)/list/list.h
variable.$(OBJEXT): $(CCAN_DIR)/str/str.h
variable.$(OBJEXT): $(hdrdir)/ruby/ruby.h
variable.$(OBJEXT): $(top_srcdir)/internal/array.h
+variable.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
variable.$(OBJEXT): $(top_srcdir)/internal/class.h
variable.$(OBJEXT): $(top_srcdir)/internal/compilers.h
variable.$(OBJEXT): $(top_srcdir)/internal/error.h
@@ -16349,6 +16896,7 @@ variable.$(OBJEXT): {$(VPATH)}ractor.h
variable.$(OBJEXT): {$(VPATH)}ractor_core.h
variable.$(OBJEXT): {$(VPATH)}ruby_assert.h
variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+variable.$(OBJEXT): {$(VPATH)}shape.h
variable.$(OBJEXT): {$(VPATH)}st.h
variable.$(OBJEXT): {$(VPATH)}subst.h
variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16369,14 +16917,16 @@ version.$(OBJEXT): $(hdrdir)/ruby.h
version.$(OBJEXT): $(hdrdir)/ruby/ruby.h
version.$(OBJEXT): $(hdrdir)/ruby/version.h
version.$(OBJEXT): $(top_srcdir)/internal/array.h
+version.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+version.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
version.$(OBJEXT): $(top_srcdir)/internal/compilers.h
version.$(OBJEXT): $(top_srcdir)/internal/gc.h
version.$(OBJEXT): $(top_srcdir)/internal/imemo.h
version.$(OBJEXT): $(top_srcdir)/internal/serial.h
version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+version.$(OBJEXT): $(top_srcdir)/internal/variable.h
version.$(OBJEXT): $(top_srcdir)/internal/vm.h
version.$(OBJEXT): $(top_srcdir)/internal/warnings.h
-version.$(OBJEXT): $(top_srcdir)/revision.h
version.$(OBJEXT): $(top_srcdir)/version.h
version.$(OBJEXT): {$(VPATH)}assert.h
version.$(OBJEXT): {$(VPATH)}atomic.h
@@ -16390,9 +16940,11 @@ version.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
version.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
version.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
version.$(OBJEXT): {$(VPATH)}config.h
+version.$(OBJEXT): {$(VPATH)}constant.h
version.$(OBJEXT): {$(VPATH)}debug_counter.h
version.$(OBJEXT): {$(VPATH)}defines.h
version.$(OBJEXT): {$(VPATH)}id.h
+version.$(OBJEXT): {$(VPATH)}id_table.h
version.$(OBJEXT): {$(VPATH)}intern.h
version.$(OBJEXT): {$(VPATH)}internal.h
version.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -16539,8 +17091,10 @@ version.$(OBJEXT): {$(VPATH)}method.h
version.$(OBJEXT): {$(VPATH)}missing.h
version.$(OBJEXT): {$(VPATH)}mjit.h
version.$(OBJEXT): {$(VPATH)}node.h
+version.$(OBJEXT): {$(VPATH)}revision.h
version.$(OBJEXT): {$(VPATH)}ruby_assert.h
version.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+version.$(OBJEXT): {$(VPATH)}shape.h
version.$(OBJEXT): {$(VPATH)}st.h
version.$(OBJEXT): {$(VPATH)}subst.h
version.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16556,6 +17110,7 @@ vm.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm.$(OBJEXT): $(hdrdir)/ruby.h
vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm.$(OBJEXT): $(top_srcdir)/internal/bignum.h
vm.$(OBJEXT): $(top_srcdir)/internal/bits.h
vm.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -16776,6 +17331,7 @@ vm.$(OBJEXT): {$(VPATH)}ractor.h
vm.$(OBJEXT): {$(VPATH)}ractor_core.h
vm.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm.$(OBJEXT): {$(VPATH)}shape.h
vm.$(OBJEXT): {$(VPATH)}st.h
vm.$(OBJEXT): {$(VPATH)}subst.h
vm.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -16805,6 +17361,7 @@ vm_backtrace.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/error.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/gc.h
@@ -16812,6 +17369,7 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h
@@ -16826,11 +17384,13 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_backtrace.$(OBJEXT): {$(VPATH)}config.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}constant.h
vm_backtrace.$(OBJEXT): {$(VPATH)}debug.h
vm_backtrace.$(OBJEXT): {$(VPATH)}defines.h
vm_backtrace.$(OBJEXT): {$(VPATH)}encoding.h
vm_backtrace.$(OBJEXT): {$(VPATH)}eval_intern.h
vm_backtrace.$(OBJEXT): {$(VPATH)}id.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}id_table.h
vm_backtrace.$(OBJEXT): {$(VPATH)}intern.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -16990,6 +17550,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h
vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}shape.h
vm_backtrace.$(OBJEXT): {$(VPATH)}st.h
vm_backtrace.$(OBJEXT): {$(VPATH)}subst.h
vm_backtrace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17003,6 +17564,7 @@ vm_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_dump.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_dump.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_dump.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/imemo.h
@@ -17180,6 +17742,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}ractor.h
vm_dump.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_dump.$(OBJEXT): {$(VPATH)}shape.h
vm_dump.$(OBJEXT): {$(VPATH)}st.h
vm_dump.$(OBJEXT): {$(VPATH)}subst.h
vm_dump.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17194,11 +17757,13 @@ vm_sync.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_sync.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_sync.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_sync.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+vm_sync.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_sync.$(OBJEXT): {$(VPATH)}assert.h
@@ -17213,6 +17778,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_sync.$(OBJEXT): {$(VPATH)}config.h
+vm_sync.$(OBJEXT): {$(VPATH)}constant.h
vm_sync.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_sync.$(OBJEXT): {$(VPATH)}defines.h
vm_sync.$(OBJEXT): {$(VPATH)}gc.h
@@ -17367,6 +17933,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}ractor.h
vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_sync.$(OBJEXT): {$(VPATH)}shape.h
vm_sync.$(OBJEXT): {$(VPATH)}st.h
vm_sync.$(OBJEXT): {$(VPATH)}subst.h
vm_sync.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17383,6 +17950,7 @@ vm_trace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/array.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/hash.h
@@ -17390,6 +17958,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
vm_trace.$(OBJEXT): {$(VPATH)}assert.h
@@ -17405,12 +17974,14 @@ vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_trace.$(OBJEXT): {$(VPATH)}builtin.h
vm_trace.$(OBJEXT): {$(VPATH)}config.h
+vm_trace.$(OBJEXT): {$(VPATH)}constant.h
vm_trace.$(OBJEXT): {$(VPATH)}debug.h
vm_trace.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_trace.$(OBJEXT): {$(VPATH)}defines.h
vm_trace.$(OBJEXT): {$(VPATH)}encoding.h
vm_trace.$(OBJEXT): {$(VPATH)}eval_intern.h
vm_trace.$(OBJEXT): {$(VPATH)}id.h
+vm_trace.$(OBJEXT): {$(VPATH)}id_table.h
vm_trace.$(OBJEXT): {$(VPATH)}intern.h
vm_trace.$(OBJEXT): {$(VPATH)}internal.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/abi.h
@@ -17572,6 +18143,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_trace.$(OBJEXT): {$(VPATH)}ractor.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_trace.$(OBJEXT): {$(VPATH)}shape.h
vm_trace.$(OBJEXT): {$(VPATH)}st.h
vm_trace.$(OBJEXT): {$(VPATH)}subst.h
vm_trace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17587,9 +18159,11 @@ yjit.$(OBJEXT): $(CCAN_DIR)/list/list.h
yjit.$(OBJEXT): $(CCAN_DIR)/str/str.h
yjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
yjit.$(OBJEXT): $(top_srcdir)/internal/array.h
+yjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
yjit.$(OBJEXT): $(top_srcdir)/internal/class.h
yjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
yjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+yjit.$(OBJEXT): $(top_srcdir)/internal/cont.h
yjit.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
yjit.$(OBJEXT): $(top_srcdir)/internal/gc.h
yjit.$(OBJEXT): $(top_srcdir)/internal/hash.h
@@ -17787,6 +18361,7 @@ yjit.$(OBJEXT): {$(VPATH)}probes.h
yjit.$(OBJEXT): {$(VPATH)}probes_helper.h
yjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
yjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+yjit.$(OBJEXT): {$(VPATH)}shape.h
yjit.$(OBJEXT): {$(VPATH)}st.h
yjit.$(OBJEXT): {$(VPATH)}subst.h
yjit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -17799,6 +18374,5 @@ yjit.$(OBJEXT): {$(VPATH)}vm_opts.h
yjit.$(OBJEXT): {$(VPATH)}vm_sync.h
yjit.$(OBJEXT): {$(VPATH)}yjit.c
yjit.$(OBJEXT): {$(VPATH)}yjit.h
-yjit.$(OBJEXT): {$(VPATH)}yjit.rb
yjit.$(OBJEXT): {$(VPATH)}yjit.rbinc
# AUTOGENERATED DEPENDENCIES END
diff --git a/compar.c b/compar.c
index 00e79b5767..040f77975e 100644
--- a/compar.c
+++ b/compar.c
@@ -50,7 +50,7 @@ VALUE
rb_invcmp(VALUE x, VALUE y)
{
VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
- if (invcmp == Qundef || NIL_P(invcmp)) {
+ if (NIL_OR_UNDEF_P(invcmp)) {
return Qnil;
}
else {
diff --git a/compile.c b/compile.c
index 484399abc6..0452305923 100644
--- a/compile.c
+++ b/compile.c
@@ -766,7 +766,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
end->rescued = LABEL_RESCUE_END;
ADD_TRACE(ret, RUBY_EVENT_B_CALL);
- NODE dummy_line_node = generate_dummy_line_node(FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), -1);
+ NODE dummy_line_node = generate_dummy_line_node(ISEQ_BODY(iseq)->location.first_lineno, -1);
ADD_INSN (ret, &dummy_line_node, nop);
ADD_LABEL(ret, start);
CHECK(COMPILE(ret, "block body", node->nd_body));
@@ -1243,6 +1243,32 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
return adjust;
}
+static void
+iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data)
+{
+ const char *types = insn_op_types(insn->insn_id);
+ for (int j = 0; types[j]; j++) {
+ char type = types[j];
+ switch (type) {
+ case TS_CDHASH:
+ case TS_ISEQ:
+ case TS_VALUE:
+ case TS_IC: // constant path array
+ case TS_CALLDATA: // ci is stored.
+ func(&OPERAND_AT(insn, j), data);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+iseq_insn_each_object_write_barrier(VALUE *obj_ptr, VALUE iseq)
+{
+ RB_OBJ_WRITTEN(iseq, Qundef, *obj_ptr);
+}
+
static INSN *
new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
int insn_id, int argc, VALUE *argv)
@@ -1260,6 +1286,9 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
iobj->operands = argv;
iobj->operand_size = argc;
iobj->sc_state = 0;
+
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
+
return iobj;
}
@@ -1333,7 +1362,7 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
ret_iseq = rb_iseq_new_with_opt(&ast, name,
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(line_no), parent,
+ line_no, parent,
isolated_depth ? isolated_depth + 1 : 0,
type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq]< ---------------------------------------\n");
@@ -1349,7 +1378,7 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call
debugs("[new_child_iseq_with_callback]> ---------------------------------------\n");
ret_iseq = rb_iseq_new_with_callback(ifunc, name,
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
+ line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq_with_callback]< ---------------------------------------\n");
return ret_iseq;
}
@@ -1357,18 +1386,18 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call
static void
set_catch_except_p(struct rb_iseq_constant_body *body)
{
- body->catch_except_p = TRUE;
+ body->catch_except_p = true;
if (body->parent_iseq != NULL) {
set_catch_except_p(ISEQ_BODY(body->parent_iseq));
}
}
-/* Set body->catch_except_p to TRUE if the ISeq may catch an exception. If it is FALSE,
- JIT-ed code may be optimized. If we are extremely conservative, we should set TRUE
+/* Set body->catch_except_p to true if the ISeq may catch an exception. If it is false,
+ JIT-ed code may be optimized. If we are extremely conservative, we should set true
if catch table exists. But we want to optimize while loop, which always has catch
table entries for break/next/redo.
- So this function sets TRUE for limited ISeqs with break/next/redo catch table entries
+ So this function sets true for limited ISeqs with break/next/redo catch table entries
whose child ISeq would really raise an exception. */
static void
update_catch_except_flags(struct rb_iseq_constant_body *body)
@@ -1399,7 +1428,7 @@ update_catch_except_flags(struct rb_iseq_constant_body *body)
if (entry->type != CATCH_TYPE_BREAK
&& entry->type != CATCH_TYPE_NEXT
&& entry->type != CATCH_TYPE_REDO) {
- body->catch_except_p = TRUE;
+ body->catch_except_p = true;
break;
}
}
@@ -2058,20 +2087,7 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
static inline VALUE
get_ivar_ic_value(rb_iseq_t *iseq,ID id)
{
- VALUE val;
- struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
- if (tbl) {
- if (rb_id_table_lookup(tbl,id,&val)) {
- return val;
- }
- }
- else {
- tbl = rb_id_table_create(1);
- ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
- }
- val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
- rb_id_table_insert(tbl,id,val);
- return val;
+ return INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
}
static inline VALUE
@@ -2251,6 +2267,30 @@ add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions
return TRUE;
}
+static ID *
+array_to_idlist(VALUE arr)
+{
+ RUBY_ASSERT(RB_TYPE_P(arr, T_ARRAY));
+ long size = RARRAY_LEN(arr);
+ ID *ids = (ID *)ALLOC_N(ID, size + 1);
+ for (int i = 0; i < size; i++) {
+ VALUE sym = RARRAY_AREF(arr, i);
+ ids[i] = SYM2ID(sym);
+ }
+ ids[size] = 0;
+ return ids;
+}
+
+static VALUE
+idlist_to_array(const ID *ids)
+{
+ VALUE arr = rb_ary_new();
+ while (*ids) {
+ rb_ary_push(arr, ID2SYM(*ids++));
+ }
+ return arr;
+}
+
/**
ruby insn object list -> raw instruction sequence
*/
@@ -2284,9 +2324,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
if (ISEQ_COVERAGE(iseq)) {
if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
!(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
- int line = iobj->insn_info.line_no;
- if (line >= 1) {
- RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0));
+ int line = iobj->insn_info.line_no - 1;
+ if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) {
+ RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0));
}
}
if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
@@ -2433,9 +2473,38 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
/* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */
case TS_IC: /* inline cache: constants */
+ {
+ unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++;
+ IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
+ if (UNLIKELY(ic_index >= body->ic_size)) {
+ BADINSN_DUMP(anchor, &iobj->link, 0);
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
+ "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
+ ic_index, ISEQ_IS_SIZE(body));
+ }
+
+ ic->segments = array_to_idlist(operands[j]);
+
+ generated_iseq[code_index + 1 + j] = (VALUE)ic;
+ }
+ break;
+ case TS_IVC: /* inline ivar cache */
+ {
+ unsigned int ic_index = FIX2UINT(operands[j]);
+
+ IVC cache = ((IVC)&body->is_entries[ic_index]);
+
+ if (insn == BIN(setinstancevariable)) {
+ cache->iv_set_name = SYM2ID(operands[j - 1]);
+ }
+ else {
+ cache->iv_set_name = 0;
+ }
+
+ vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
+ }
case TS_ISE: /* inline storage entry: `once` insn */
case TS_ICVARC: /* inline cvar cache */
- case TS_IVC: /* inline ivar cache */
{
unsigned int ic_index = FIX2UINT(operands[j]);
IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
@@ -2447,11 +2516,6 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
generated_iseq[code_index + 1 + j] = (VALUE)ic;
- if (insn == BIN(opt_getinlinecache) && type == TS_IC) {
- // Store the instruction index for opt_getinlinecache on the IC for
- // YJIT to invalidate code when opt_setinlinecache runs.
- ic->get_insn_idx = (unsigned int)code_index;
- }
break;
}
case TS_CALLDATA:
@@ -2596,9 +2660,11 @@ iseq_set_exception_table(rb_iseq_t *iseq)
struct iseq_catch_table_entry *entry;
ISEQ_BODY(iseq)->catch_table = NULL;
- if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK;
- tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
- tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
+
+ VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
+ if (NIL_P(catch_table_ary)) return COMPILE_OK;
+ tlen = (int)RARRAY_LEN(catch_table_ary);
+ tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary);
if (tlen > 0) {
struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
@@ -2634,6 +2700,8 @@ iseq_set_exception_table(rb_iseq_t *iseq)
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
}
+ RB_GC_GUARD(catch_table_ary);
+
return COMPILE_OK;
}
@@ -2772,7 +2840,6 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
break;
}
else if ((lab = find_destination((INSN *)i)) != 0) {
- if (lab->unremovable) break;
unref_counts[lab->label_no]++;
}
}
@@ -2789,8 +2856,7 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
/* do nothing */
}
else if (IS_ADJUST(i)) {
- LABEL *dest = ((ADJUST *)i)->label;
- if (dest && dest->unremovable) return 0;
+ return 0;
}
end = i;
} while ((i = i->next) != 0);
@@ -3537,6 +3603,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
if (IS_INSN_ID(iobj, dup)) {
if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
+
+ /*
+ * dup
+ * setlocal x, y
+ * setlocal x, y
+ * =>
+ * dup
+ * setlocal x, y
+ */
if (IS_NEXT_INSN_ID(set1, setlocal)) {
set2 = set1->next;
if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
@@ -3545,6 +3620,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
ELEM_REMOVE(&iobj->link);
}
}
+
+ /*
+ * dup
+ * setlocal x, y
+ * dup
+ * setlocal x, y
+ * =>
+ * dup
+ * setlocal x, y
+ */
else if (IS_NEXT_INSN_ID(set1, dup) &&
IS_NEXT_INSN_ID(set1->next, setlocal)) {
set2 = set1->next->next;
@@ -3557,6 +3642,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ /*
+ * getlocal x, y
+ * dup
+ * setlocal x, y
+ * =>
+ * dup
+ */
if (IS_INSN_ID(iobj, getlocal)) {
LINK_ELEMENT *niobj = &iobj->link;
if (IS_NEXT_INSN_ID(niobj, dup)) {
@@ -3572,6 +3664,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ /*
+ * opt_invokebuiltin_delegate
+ * trace
+ * leave
+ * =>
+ * opt_invokebuiltin_delegate_leave
+ * trace
+ * leave
+ */
if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
if (IS_TRACE(iobj->link.next)) {
if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
@@ -3580,6 +3681,19 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ /*
+ * getblockparam
+ * branchif / branchunless
+ * =>
+ * getblockparamproxy
+ * branchif / branchunless
+ */
+ if (IS_INSN_ID(iobj, getblockparam)) {
+ if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) {
+ iobj->insn_id = BIN(getblockparamproxy);
+ }
+ }
+
return COMPILE_OK;
}
@@ -4506,7 +4620,12 @@ compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
}
else {
CHECK(COMPILE_(ret, "array element", node, FALSE));
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ if (keyword_node_p(node)) {
+ ADD_INSN1(ret, node, newarraykwsplat, INT2FIX(1));
+ }
+ else {
+ ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ }
}
return 1;
@@ -4747,7 +4866,7 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
const NODE *val = vals->nd_head;
VALUE lit = rb_node_case_when_optimizable_literal(val);
- if (lit == Qundef) {
+ if (UNDEF_P(lit)) {
only_special_literals = 0;
}
else if (NIL_P(rb_hash_lookup(literals, lit))) {
@@ -4957,12 +5076,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
+ bool safenav_call = false;
LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
ASSUME(iobj);
- ELEM_REMOVE(LAST_ELEMENT(pre));
- ELEM_REMOVE((LINK_ELEMENT *)iobj);
- pre->last = iobj->link.prev;
+ ELEM_REMOVE(insn_element);
+ if (!IS_INSN_ID(iobj, send)) {
+ safenav_call = true;
+ iobj = (INSN *)get_prev_insn(iobj);
+ ELEM_INSERT_NEXT(&iobj->link, insn_element);
+ }
+ (pre->last = iobj->link.prev)->next = 0;
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
int argc = vm_ci_argc(ci) + 1;
@@ -4981,7 +5105,9 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
return COMPILE_NG;
}
- ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
+ iobj->link.prev = lhs->last;
+ lhs->last->next = &iobj->link;
+ for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
int argc = vm_ci_argc(ci);
ci = ci_argc_set(iseq, ci, argc - 1);
@@ -4990,9 +5116,11 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
INSERT_BEFORE_INSN(iobj, line_node, concatarray);
}
- ADD_INSN(lhs, line_node, pop);
- if (argc != 1) {
+ if (!safenav_call) {
ADD_INSN(lhs, line_node, pop);
+ if (argc != 1) {
+ ADD_INSN(lhs, line_node, pop);
+ }
}
for (int i=0; i < argc; i++) {
ADD_INSN(post, line_node, pop);
@@ -5233,6 +5361,29 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
return COMPILE_OK;
}
+static VALUE
+collect_const_segments(rb_iseq_t *iseq, const NODE *node)
+{
+ VALUE arr = rb_ary_new();
+ for (;;) {
+ switch (nd_type(node)) {
+ case NODE_CONST:
+ rb_ary_unshift(arr, ID2SYM(node->nd_vid));
+ return arr;
+ case NODE_COLON3:
+ rb_ary_unshift(arr, ID2SYM(node->nd_mid));
+ rb_ary_unshift(arr, ID2SYM(idNULL));
+ return arr;
+ case NODE_COLON2:
+ rb_ary_unshift(arr, ID2SYM(node->nd_mid));
+ node = node->nd_head;
+ break;
+ default:
+ return Qfalse;
+ }
+ }
+}
+
static int
compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
@@ -7298,7 +7449,7 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ADD_LABEL(ret, end_label);
ADD_ADJUST_RESTORE(ret, adjust_label);
- if (node->nd_state == Qundef) {
+ if (UNDEF_P(node->nd_state)) {
/* ADD_INSN(ret, line_node, putundef); */
COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
return COMPILE_NG;
@@ -7353,7 +7504,30 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ISEQ_TYPE_BLOCK, line);
CHECK(COMPILE(ret, "iter caller", node->nd_iter));
}
- ADD_LABEL(ret, retry_end_l);
+
+ {
+ // We need to put the label "retry_end_l" immediately after the last "send" instruction.
+ // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
+ // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
+ //
+ // Normally, "send" instruction is at the last.
+ // However, qcall under branch coverage measurement adds some instructions after the "send".
+ //
+ // Note that "invokesuper" appears instead of "send".
+ INSN *iobj;
+ LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
+ iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
+ while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
+ iobj = (INSN*) get_prev_insn(iobj);
+ }
+ ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
+
+ // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
+ // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
+ if (&iobj->link == LAST_ELEMENT(ret)) {
+ ret->last = (LINK_ELEMENT*) retry_end_l;
+ }
+ }
if (popped) {
ADD_INSN(ret, line_node, pop);
@@ -7483,7 +7657,6 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
add_ensure_iseq(ret, iseq, 0);
ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
ADD_ADJUST_RESTORE(ret, splabel);
- splabel->unremovable = FALSE;
if (!popped) {
ADD_INSN(ret, line_node, putnil);
@@ -8126,7 +8299,7 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N
ISEQ_BODY(iseq)->mandatory_only_iseq =
rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq),
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
- INT2FIX(nd_line(line_node)), NULL, 0,
+ nd_line(line_node), NULL, 0,
ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option);
GET_VM()->builtin_inline_index = prev_inline_index;
@@ -8558,6 +8731,17 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
lfin: # o ?
pop # o
+ # or (popped)
+ if lcfin # r
+ eval v # r v
+ send a= # ?
+ jump lfin # ?
+
+ lcfin: # r
+
+ lfin: # ?
+ pop #
+
# and
dup # r o o
unless lcfin
@@ -8586,32 +8770,32 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
if (atype == idOROP || atype == idANDOP) {
- ADD_INSN(ret, node, dup);
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
if (atype == idOROP) {
ADD_INSNL(ret, node, branchif, lcfin);
}
else { /* idANDOP */
ADD_INSNL(ret, node, branchunless, lcfin);
}
- ADD_INSN(ret, node, pop);
+ if (!popped) {
+ ADD_INSN(ret, node, pop);
+ }
CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
- ADD_INSN(ret, node, swap);
- ADD_INSN1(ret, node, topn, INT2FIX(1));
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, topn, INT2FIX(1));
+ }
ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
ADD_INSNL(ret, node, jump, lfin);
ADD_LABEL(ret, lcfin);
- ADD_INSN(ret, node, swap);
+ if (!popped) {
+ ADD_INSN(ret, node, swap);
+ }
ADD_LABEL(ret, lfin);
- ADD_INSN(ret, node, pop);
- if (lskip) {
- ADD_LABEL(ret, lskip);
- }
- if (popped) {
- /* we can apply more optimize */
- ADD_INSN(ret, node, pop);
- }
}
else {
CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
@@ -8621,13 +8805,13 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_INSN1(ret, node, topn, INT2FIX(1));
}
ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
- if (lskip && popped) {
- ADD_LABEL(ret, lskip);
- }
- ADD_INSN(ret, node, pop);
- if (lskip && !popped) {
- ADD_LABEL(ret, lskip);
- }
+ }
+ if (lskip && popped) {
+ ADD_LABEL(ret, lskip);
+ }
+ ADD_INSN(ret, node, pop);
+ if (lskip && !popped) {
+ ADD_LABEL(ret, lskip);
}
return COMPILE_OK;
}
@@ -8726,7 +8910,10 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
}
CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head));
- ADD_INSN(ret, node, dup);
+
+ if (!popped) {
+ ADD_INSN(ret, node, dup);
+ }
if (type == NODE_OP_ASGN_AND) {
ADD_INSNL(ret, node, branchunless, lfin);
@@ -8735,15 +8922,13 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
ADD_INSNL(ret, node, branchif, lfin);
}
- ADD_INSN(ret, node, pop);
- ADD_LABEL(ret, lassign);
- CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value));
- ADD_LABEL(ret, lfin);
-
- if (popped) {
- /* we can apply more optimize */
+ if (!popped) {
ADD_INSN(ret, node, pop);
}
+
+ ADD_LABEL(ret, lassign);
+ CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped));
+ ADD_LABEL(ret, lfin);
return COMPILE_OK;
}
@@ -8970,37 +9155,32 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
static int
compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- const int line = nd_line(node);
if (rb_is_const_id(node->nd_mid)) {
/* constant */
- LABEL *lend = NEW_LABEL(line);
- int ic_index = ISEQ_BODY(iseq)->ic_size++;
-
- DECL_ANCHOR(pref);
- DECL_ANCHOR(body);
-
- INIT_ANCHOR(pref);
- INIT_ANCHOR(body);
- CHECK(compile_const_prefix(iseq, node, pref, body));
- if (LIST_INSN_SIZE_ZERO(pref)) {
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- }
- else {
+ VALUE segments;
+ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
+ (segments = collect_const_segments(iseq, node))) {
+ ISEQ_BODY(iseq)->ic_size++;
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
+ }
+ else {
+ /* constant */
+ DECL_ANCHOR(pref);
+ DECL_ANCHOR(body);
+
+ INIT_ANCHOR(pref);
+ INIT_ANCHOR(body);
+ CHECK(compile_const_prefix(iseq, node, pref, body));
+ if (LIST_INSN_SIZE_ZERO(pref)) {
ADD_INSN(ret, node, putnil);
+ ADD_SEQ(ret, body);
}
-
- ADD_SEQ(ret, body);
-
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
+ else {
+ ADD_SEQ(ret, pref);
+ ADD_SEQ(ret, body);
}
}
- else {
- ADD_SEQ(ret, pref);
- ADD_SEQ(ret, body);
- }
}
else {
/* function call */
@@ -9017,25 +9197,19 @@ compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
static int
compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- const int line = nd_line(node);
- LABEL *lend = NEW_LABEL(line);
- int ic_index = ISEQ_BODY(iseq)->ic_size++;
-
debugi("colon3#nd_mid", node->nd_mid);
/* add cache insn */
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- ADD_INSN(ret, node, pop);
+ ISEQ_BODY(iseq)->ic_size++;
+ VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(node->nd_mid));
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
}
-
- ADD_INSN1(ret, node, putobject, rb_cObject);
- ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
-
- if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
+ else {
+ ADD_INSN1(ret, node, putobject, rb_cObject);
+ ADD_INSN1(ret, node, putobject, Qtrue);
+ ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
}
if (popped) {
@@ -9147,7 +9321,8 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
*/
- if (mid == idASET && !private_recv_p(node) && node->nd_args &&
+ if (!ISEQ_COMPILE_DATA(iseq)->in_masgn &&
+ mid == idASET && !private_recv_p(node) && node->nd_args &&
nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 &&
nd_type_p(node->nd_args->nd_head, NODE_STR) &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
@@ -9345,7 +9520,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_MASGN:{
+ bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn;
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = true;
compile_massign(iseq, ret, node, popped);
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn;
break;
}
@@ -9537,14 +9715,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
debugi("nd_vid", node->nd_vid);
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- LABEL *lend = NEW_LABEL(line);
- int ic_index = body->ic_size++;
-
- ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index));
- ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
- ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index));
- ADD_LABEL(ret, lend);
+ body->ic_size++;
+ VALUE segments = rb_ary_new_from_args(1, ID2SYM(node->nd_vid));
+ ADD_INSN1(ret, node, opt_getconstant_path, segments);
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
}
else {
ADD_INSN(ret, node, putnil);
@@ -9591,7 +9765,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_LIT:{
debugp_param("lit", node->nd_lit);
if (!popped) {
- ADD_INSN1(ret, node, putobject, node->nd_lit);
+ if (UNLIKELY(node->nd_lit == rb_mRubyVMFrozenCore)) {
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); // [Bug #20569]
+ }
+ else {
+ ADD_INSN1(ret, node, putobject, node->nd_lit);
+ }
RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit);
}
break;
@@ -10032,10 +10211,16 @@ insn_data_to_s_detail(INSN *iobj)
rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
break;
case TS_IC: /* inline cache */
+ rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
+ break;
case TS_IVC: /* inline ivar cache */
+ rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ break;
case TS_ICVARC: /* inline cvar cache */
+ rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ break;
case TS_ISE: /* inline storage entry */
- rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
+ rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
break;
case TS_CALLDATA: /* we store these as call infos at compile time */
{
@@ -10431,9 +10616,20 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
}
break;
case TS_IC:
- argv[j] = op;
- if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ic_size) {
- ISEQ_BODY(iseq)->ic_size = NUM2INT(op) + 1;
+ {
+ VALUE segments = rb_ary_new();
+ op = rb_to_array_type(op);
+
+ for (int i = 0; i < RARRAY_LEN(op); i++) {
+ VALUE sym = RARRAY_AREF(op, i);
+ sym = rb_to_symbol_type(sym);
+ rb_ary_push(segments, sym);
+ }
+
+ RB_GC_GUARD(op);
+ argv[j] = segments;
+ RB_OBJ_WRITTEN(iseq, Qundef, segments);
+ ISEQ_BODY(iseq)->ic_size++;
}
break;
case TS_IVC: /* inline ivar cache */
@@ -10592,6 +10788,12 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
return keyword;
}
+static void
+iseq_insn_each_object_mark(VALUE *obj_ptr, VALUE _)
+{
+ rb_gc_mark(*obj_ptr);
+}
+
void
rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
{
@@ -10618,28 +10820,7 @@ rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage)
iobj = (INSN *)&storage->buff[pos];
if (iobj->operands) {
- int j;
- const char *types = insn_op_types(iobj->insn_id);
-
- for (j = 0; types[j]; j++) {
- char type = types[j];
- switch (type) {
- case TS_CDHASH:
- case TS_ISEQ:
- case TS_VALUE:
- case TS_CALLDATA: // ci is stored.
- {
- VALUE op = OPERAND_AT(iobj, j);
-
- if (!SPECIAL_CONST_P(op)) {
- rb_gc_mark(op);
- }
- }
- break;
- default:
- break;
- }
- }
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark, (VALUE)0);
}
pos += (int)size;
}
@@ -11255,6 +11436,12 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
break;
case TS_IC:
+ {
+ IC ic = (IC)op;
+ VALUE arr = idlist_to_array(ic->segments);
+ wv = ibf_dump_object(dump, arr);
+ }
+ break;
case TS_ISE:
case TS_IVC:
case TS_ICVARC:
@@ -11299,6 +11486,7 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
struct rb_call_data *cd_entries = load_body->call_data;
+ int ic_index = 0;
iseq_bits_t * mark_offset_bits;
@@ -11315,7 +11503,6 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
for (code_index=0; code_index<iseq_size;) {
/* opcode */
const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
- const unsigned int insn_index = code_index;
const char *types = insn_op_types(insn);
int op_index;
@@ -11370,6 +11557,16 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
break;
}
case TS_IC:
+ {
+ VALUE op = ibf_load_small_value(load, &reading_pos);
+ VALUE arr = ibf_load_object(load, op);
+
+ IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
+ ic->segments = array_to_idlist(arr);
+
+ code[code_index] = (VALUE)ic;
+ }
+ break;
case TS_ISE:
case TS_ICVARC:
case TS_IVC:
@@ -11379,11 +11576,20 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
code[code_index] = (VALUE)ic;
- if (insn == BIN(opt_getinlinecache) && operand_type == TS_IC) {
- // Store the instruction index for opt_getinlinecache on the IC for
- // YJIT to invalidate code when opt_setinlinecache runs.
- ic->ic_cache.get_insn_idx = insn_index;
+ if (operand_type == TS_IVC) {
+ IVC cache = (IVC)ic;
+
+ if (insn == BIN(setinstancevariable)) {
+ ID iv_name = (ID)code[code_index - 1];
+ cache->iv_set_name = iv_name;
+ }
+ else {
+ cache->iv_set_name = 0;
+ }
+
+ vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
}
+
}
break;
case TS_CALLDATA:
@@ -12001,7 +12207,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
- const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos);
+ const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
@@ -12031,6 +12237,40 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos);
const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos);
+ // setup fname and dummy frame
+ VALUE path = ibf_load_object(load, location_pathobj_index);
+ {
+ VALUE realpath = Qnil;
+
+ if (RB_TYPE_P(path, T_STRING)) {
+ realpath = path = rb_fstring(path);
+ }
+ else if (RB_TYPE_P(path, T_ARRAY)) {
+ VALUE pathobj = path;
+ if (RARRAY_LEN(pathobj) != 2) {
+ rb_raise(rb_eRuntimeError, "path object size mismatch");
+ }
+ path = rb_fstring(RARRAY_AREF(pathobj, 0));
+ realpath = RARRAY_AREF(pathobj, 1);
+ if (!NIL_P(realpath)) {
+ if (!RB_TYPE_P(realpath, T_STRING)) {
+ rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
+ "(%x), path=%+"PRIsVALUE,
+ realpath, TYPE(realpath), path);
+ }
+ realpath = rb_fstring(realpath);
+ }
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "unexpected path object");
+ }
+ rb_iseq_pathobj_set(iseq, path, realpath);
+ }
+
+ // push dummy frame
+ rb_execution_context_t *ec = GET_EC();
+ VALUE dummy_frame = rb_vm_push_frame_fname(ec, path);
+
#undef IBF_BODY_OFFSET
load_body->type = type;
@@ -12099,33 +12339,6 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load->current_buffer = &load->global_buffer;
#endif
- {
- VALUE realpath = Qnil, path = ibf_load_object(load, location_pathobj_index);
- if (RB_TYPE_P(path, T_STRING)) {
- realpath = path = rb_fstring(path);
- }
- else if (RB_TYPE_P(path, T_ARRAY)) {
- VALUE pathobj = path;
- if (RARRAY_LEN(pathobj) != 2) {
- rb_raise(rb_eRuntimeError, "path object size mismatch");
- }
- path = rb_fstring(RARRAY_AREF(pathobj, 0));
- realpath = RARRAY_AREF(pathobj, 1);
- if (!NIL_P(realpath)) {
- if (!RB_TYPE_P(realpath, T_STRING)) {
- rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
- "(%x), path=%+"PRIsVALUE,
- realpath, TYPE(realpath), path);
- }
- realpath = rb_fstring(realpath);
- }
- }
- else {
- rb_raise(rb_eRuntimeError, "unexpected path object");
- }
- rb_iseq_pathobj_set(iseq, path, realpath);
- }
-
RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
@@ -12133,6 +12346,9 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load->current_buffer = saved_buffer;
#endif
verify_call_cache(iseq);
+
+ RB_GC_GUARD(dummy_frame);
+ rb_vm_pop_frame_no_int(ec);
}
struct ibf_dump_iseq_list_arg
@@ -12883,7 +13099,7 @@ ibf_dump_memsize(const void *ptr)
static const rb_data_type_t ibf_dump_type = {
"ibf_dump",
{ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
- 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
static void
diff --git a/complex.c b/complex.c
index ae40fa7355..a227cb0a58 100644
--- a/complex.c
+++ b/complex.c
@@ -316,7 +316,7 @@ f_negative_p(VALUE x)
#define f_positive_p(x) (!f_negative_p(x))
-inline static int
+inline static bool
f_zero_p(VALUE x)
{
if (RB_FLOAT_TYPE_P(x)) {
@@ -329,7 +329,7 @@ f_zero_p(VALUE x)
const VALUE num = RRATIONAL(x)->num;
return FIXNUM_ZERO_P(num);
}
- return (int)rb_equal(x, ZERO);
+ return rb_equal(x, ZERO) != 0;
}
#define f_nonzero_p(x) (!f_zero_p(x))
@@ -421,15 +421,22 @@ f_complex_new_bang2(VALUE klass, VALUE x, VALUE y)
return nucomp_s_new_internal(klass, x, y);
}
-inline static void
+WARN_UNUSED_RESULT(inline static VALUE nucomp_real_check(VALUE num));
+inline static VALUE
nucomp_real_check(VALUE num)
{
if (!RB_INTEGER_TYPE_P(num) &&
!RB_FLOAT_TYPE_P(num) &&
!RB_TYPE_P(num, T_RATIONAL)) {
+ if (RB_TYPE_P(num, T_COMPLEX) && nucomp_real_p(num)) {
+ VALUE real = RCOMPLEX(num)->real;
+ assert(!RB_TYPE_P(real, T_COMPLEX));
+ return real;
+ }
if (!k_numeric_p(num) || !f_real_p(num))
rb_raise(rb_eTypeError, "not a real");
}
+ return num;
}
inline static VALUE
@@ -480,22 +487,26 @@ nucomp_s_new(int argc, VALUE *argv, VALUE klass)
switch (rb_scan_args(argc, argv, "11", &real, &imag)) {
case 1:
- nucomp_real_check(real);
+ real = nucomp_real_check(real);
imag = ZERO;
break;
default:
- nucomp_real_check(real);
- nucomp_real_check(imag);
+ real = nucomp_real_check(real);
+ imag = nucomp_real_check(imag);
break;
}
- return nucomp_s_canonicalize_internal(klass, real, imag);
+ return nucomp_s_new_internal(klass, real, imag);
}
inline static VALUE
f_complex_new2(VALUE klass, VALUE x, VALUE y)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
+ if (RB_TYPE_P(x, T_COMPLEX)) {
+ get_dat1(x);
+ x = dat->real;
+ y = f_add(dat->imag, y);
+ }
return nucomp_s_canonicalize_internal(klass, x, y);
}
@@ -550,7 +561,7 @@ nucomp_f_complex(int argc, VALUE *argv, VALUE klass)
if (!NIL_P(opts)) {
raise = rb_opts_exception_p(opts, raise);
}
- if (argc > 0 && CLASS_OF(a1) == rb_cComplex && a2 == Qundef) {
+ if (argc > 0 && CLASS_OF(a1) == rb_cComplex && UNDEF_P(a2)) {
return a1;
}
return nucomp_convert(rb_cComplex, a1, a2, raise);
@@ -607,10 +618,8 @@ m_sin(VALUE x)
}
static VALUE
-f_complex_polar(VALUE klass, VALUE x, VALUE y)
+f_complex_polar_real(VALUE klass, VALUE x, VALUE y)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
- assert(!RB_TYPE_P(y, T_COMPLEX));
if (f_zero_p(x) || f_zero_p(y)) {
return nucomp_s_new_internal(klass, x, RFLOAT_0);
}
@@ -646,6 +655,14 @@ f_complex_polar(VALUE klass, VALUE x, VALUE y)
f_mul(x, m_sin(y)));
}
+static VALUE
+f_complex_polar(VALUE klass, VALUE x, VALUE y)
+{
+ x = nucomp_real_check(x);
+ y = nucomp_real_check(y);
+ return f_complex_polar_real(klass, x, y);
+}
+
#ifdef HAVE___COSPI
# define cospi(x) __cospi(x)
#else
@@ -694,24 +711,15 @@ nucomp_s_polar(int argc, VALUE *argv, VALUE klass)
{
VALUE abs, arg;
- switch (rb_scan_args(argc, argv, "11", &abs, &arg)) {
- case 1:
- nucomp_real_check(abs);
- return nucomp_s_new_internal(klass, abs, ZERO);
- default:
- nucomp_real_check(abs);
- nucomp_real_check(arg);
- break;
+ argc = rb_scan_args(argc, argv, "11", &abs, &arg);
+ abs = nucomp_real_check(abs);
+ if (argc == 2) {
+ arg = nucomp_real_check(arg);
}
- if (RB_TYPE_P(abs, T_COMPLEX)) {
- get_dat1(abs);
- abs = dat->real;
- }
- if (RB_TYPE_P(arg, T_COMPLEX)) {
- get_dat1(arg);
- arg = dat->real;
+ else {
+ arg = ZERO;
}
- return f_complex_polar(klass, abs, arg);
+ return f_complex_polar_real(klass, abs, arg);
}
/*
@@ -832,7 +840,7 @@ rb_complex_minus(VALUE self, VALUE other)
}
static VALUE
-safe_mul(VALUE a, VALUE b, int az, int bz)
+safe_mul(VALUE a, VALUE b, bool az, bool bz)
{
double v;
if (!az && bz && RB_FLOAT_TYPE_P(a) && (v = RFLOAT_VALUE(a), !isnan(v))) {
@@ -847,10 +855,10 @@ safe_mul(VALUE a, VALUE b, int az, int bz)
static void
comp_mul(VALUE areal, VALUE aimag, VALUE breal, VALUE bimag, VALUE *real, VALUE *imag)
{
- int arzero = f_zero_p(areal);
- int aizero = f_zero_p(aimag);
- int brzero = f_zero_p(breal);
- int bizero = f_zero_p(bimag);
+ bool arzero = f_zero_p(areal);
+ bool aizero = f_zero_p(aimag);
+ bool brzero = f_zero_p(breal);
+ bool bizero = f_zero_p(bimag);
*real = f_sub(safe_mul(areal, breal, arzero, brzero),
safe_mul(aimag, bimag, aizero, bizero));
*imag = f_add(safe_mul(areal, bimag, arzero, bizero),
@@ -1101,7 +1109,7 @@ static bool
nucomp_real_p(VALUE self)
{
get_dat1(self);
- return(f_zero_p(dat->imag) ? true : false);
+ return f_zero_p(dat->imag);
}
/*
@@ -1124,15 +1132,23 @@ nucomp_cmp(VALUE self, VALUE other)
if (!k_numeric_p(other)) {
return rb_num_coerce_cmp(self, other, idCmp);
}
- if (nucomp_real_p(self)) {
- if (RB_TYPE_P(other, T_COMPLEX) && nucomp_real_p(other)) {
+ if (!nucomp_real_p(self)) {
+ return Qnil;
+ }
+ if (RB_TYPE_P(other, T_COMPLEX)) {
+ if (nucomp_real_p(other)) {
get_dat2(self, other);
return rb_funcall(adat->real, idCmp, 1, bdat->real);
}
- else if (f_real_p(other)) {
- get_dat1(self);
+ }
+ else {
+ get_dat1(self);
+ if (f_real_p(other)) {
return rb_funcall(dat->real, idCmp, 1, other);
}
+ else {
+ return rb_num_coerce_cmp(dat->real, other, idCmp);
+ }
}
return Qnil;
}
@@ -1735,9 +1751,9 @@ read_digits(const char **s, int strict,
while (isdecimal(**s) || **s == '_') {
if (**s == '_') {
- if (strict) {
- if (us)
- return 0;
+ if (us) {
+ if (strict) return 0;
+ break;
}
us = 1;
}
@@ -2016,6 +2032,12 @@ string_to_c_strict(VALUE self, int raise)
* '1/2+3/4i'.to_c #=> ((1/2)+(3/4)*i)
* 'ruby'.to_c #=> (0+0i)
*
+ * Polar form:
+ * include Math
+ * "1.0@0".to_c #=> (1+0.0i)
+ * "1.0@#{PI/2}".to_c #=> (0.0+1i)
+ * "1.0@#{PI}".to_c #=> (-1+0.0i)
+ *
* See Kernel.Complex.
*/
static VALUE
@@ -2085,17 +2107,20 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
}
if (RB_TYPE_P(a1, T_COMPLEX)) {
- if (a2 == Qundef || (k_exact_zero_p(a2)))
+ if (UNDEF_P(a2) || (k_exact_zero_p(a2)))
return a1;
}
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
if (k_numeric_p(a1) && !f_real_p(a1))
return a1;
/* should raise exception for consistency */
if (!k_numeric_p(a1)) {
- if (!raise)
- return rb_protect(to_complex, a1, NULL);
+ if (!raise) {
+ a1 = rb_protect(to_complex, a1, NULL);
+ rb_set_errinfo(Qnil);
+ return a1;
+ }
return to_complex(a1);
}
}
@@ -2111,7 +2136,7 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
int argc;
VALUE argv2[2];
argv2[0] = a1;
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
argv2[1] = Qnil;
argc = 1;
}
@@ -2139,31 +2164,6 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * num.real -> self
- *
- * Returns self.
- */
-static VALUE
-numeric_real(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
- * num.imag -> 0
- * num.imaginary -> 0
- *
- * Returns zero.
- */
-static VALUE
-numeric_imag(VALUE self)
-{
- return INT2FIX(0);
-}
-
-/*
- * call-seq:
* num.abs2 -> real
*
* Returns square of self.
@@ -2235,19 +2235,6 @@ numeric_polar(VALUE self)
/*
* call-seq:
- * num.conj -> self
- * num.conjugate -> self
- *
- * Returns self.
- */
-static VALUE
-numeric_conj(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
* flo.arg -> 0 or float
* flo.angle -> 0 or float
* flo.phase -> 0 or float
@@ -2411,9 +2398,6 @@ Init_Complex(void)
rb_define_private_method(CLASS_OF(rb_cComplex), "convert", nucomp_s_convert, -1);
- rb_define_method(rb_cNumeric, "real", numeric_real, 0);
- rb_define_method(rb_cNumeric, "imaginary", numeric_imag, 0);
- rb_define_method(rb_cNumeric, "imag", numeric_imag, 0);
rb_define_method(rb_cNumeric, "abs2", numeric_abs2, 0);
rb_define_method(rb_cNumeric, "arg", numeric_arg, 0);
rb_define_method(rb_cNumeric, "angle", numeric_arg, 0);
@@ -2421,8 +2405,6 @@ Init_Complex(void)
rb_define_method(rb_cNumeric, "rectangular", numeric_rect, 0);
rb_define_method(rb_cNumeric, "rect", numeric_rect, 0);
rb_define_method(rb_cNumeric, "polar", numeric_polar, 0);
- rb_define_method(rb_cNumeric, "conjugate", numeric_conj, 0);
- rb_define_method(rb_cNumeric, "conj", numeric_conj, 0);
rb_define_method(rb_cFloat, "arg", float_arg, 0);
rb_define_method(rb_cFloat, "angle", float_arg, 0);
diff --git a/configure.ac b/configure.ac
index 0f4a31af7e..220392d120 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,14 +64,22 @@ AC_ARG_WITH(baseruby,
[
AC_PATH_PROG([BASERUBY], [ruby], [false])
])
+# BASERUBY must be >= 2.2.0. Note that `"2.2.0" > "2.2"` is true.
AS_IF([test "$HAVE_BASERUBY" != no -a "`RUBYOPT=- $BASERUBY --disable=gems -e 'print 42 if RUBY_VERSION > "2.2"' 2>/dev/null`" = 42], [
+ AS_CASE(["$build_os"], [mingw*], [
+ # Can MSys shell run a command with a drive letter?
+ RUBYOPT=- `cygpath -ma "$BASERUBY"` --disable=gems -e exit 2>/dev/null || HAVE_BASERUBY=no
+ ])
BASERUBY="$BASERUBY --disable=gems"
BASERUBY_VERSION=`$BASERUBY -v`
$BASERUBY -C "$srcdir" tool/downloader.rb -d tool -e gnu config.guess config.sub >&AS_MESSAGE_FD
], [
- BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
HAVE_BASERUBY=no
])
+AS_IF([test "$HAVE_BASERUBY" = no], [
+ AS_IF([test "$cross_compiling" = yes], [AC_MSG_ERROR([executable host ruby is required for cross-compiling])])
+ BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
+])
AC_SUBST(BASERUBY)
AC_SUBST(HAVE_BASERUBY)
@@ -125,9 +133,12 @@ AC_CANONICAL_TARGET
AS_CASE(["$target_cpu-$target_os"],
[aarch64-darwin*], [
target_cpu=arm64
- AS_CASE(["$target_vendor"], [unknown], [target_vendor=apple target=${target/-unknown-/-apple-}])
- target="${target/aarch64/arm64}"
- target_alias="${target_alias/aarch64/arm64}"
+ AS_CASE(["$target_vendor"], [unknown], [
+ target_vendor=apple
+ target=${target%%-unknown-*}-apple-${target@%:@*-unknown-}
+ ])
+ target="arm64-${target@%:@aarch64-}"
+ AS_IF([test -n "$target_alias"], [target_alias="arm64-${target_alias@%:@aarch64-}"])
])
AC_ARG_PROGRAM
@@ -203,15 +214,23 @@ AS_CASE(["/${rb_CC} "],
[*clang*], [
# Ditto for LLVM. Note however that llvm-as is a LLVM-IR to LLVM bitcode
# assembler that does not target your machine native binary.
- : ${LD:="${CC}"} # ... try -fuse-ld=lld ?
- RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/llvm-ar/])
-# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/llvm-as/])
+
+ # Xcode has its own version tools that may be incompatible with
+ # genuine LLVM tools, use the tools in the same directory.
+
+ AS_IF([$rb_CC -E -dM -xc - < /dev/null | grep -F __apple_build_version__ > /dev/null],
+ [llvm_prefix=], [llvm_prefix=llvm-])
+ # AC_PREPROC_IFELSE cannot be used before AC_USE_SYSTEM_EXTENSIONS
+
+ RUBY_CHECK_PROG_FOR_CC([LD], [s/clang/ld/]) # ... maybe try lld ?
+ RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/${llvm_prefix}ar/])
+# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/${llvm_prefix}as/])
RUBY_CHECK_PROG_FOR_CC([CXX], [s/clang/clang++/])
- RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/llvm-nm/])
- RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/llvm-objcopy/])
- RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/llvm-objdump/])
- RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/llvm-ranlib/])
- RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/llvm-strip/])
+ RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/${llvm_prefix}nm/])
+ RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/${llvm_prefix}objcopy/])
+ RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/${llvm_prefix}objdump/])
+ RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/${llvm_prefix}ranlib/])
+ RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/${llvm_prefix}strip/])
])
AS_UNSET(rb_CC)
AS_UNSET(rb_dummy)
@@ -355,12 +374,6 @@ AS_CASE(["$target_os"],
[AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
AC_MSG_ERROR([Unsupported OS X version is required])])
- AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported],
- [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])],
- [rb_cv_tls_supported=yes],
- [rb_cv_tls_supported=no])])
- AS_IF([test x"$rb_cv_tls_supported" != xyes],
- [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)])
])
RUBY_MINGW32
@@ -380,6 +393,13 @@ AS_IF([test "$GCC" = yes], [
AS_IF([test "$gcc_major" -lt 4], [
AC_MSG_ERROR([too old GCC: $gcc_major.$gcc_minor])
])
+
+ AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])],
+ [rb_cv_tls_supported=yes],
+ [rb_cv_tls_supported=no])])
+ AS_IF([test x"$rb_cv_tls_supported" != xyes],
+ [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)])
], [
linker_flag=
])
@@ -490,6 +510,7 @@ AS_CASE(["$target_os"],
AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER)
sysconfdir=
])
+ rb_cv_binary_elf=no
: ${enable_shared=yes}
],
[hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi <toyoda@npd.kishou.go.jp>
@@ -512,11 +533,16 @@ AS_IF([test "$cross_compiling:$ac_cv_prog_DTRACE" = no: -a -n "$ac_tool_prefix"]
AC_CHECK_PROGS(DOT, dot)
AC_CHECK_PROGS(DOXYGEN, doxygen)
-for prog in ${ac_tool_prefix:+${ac_tool_prefix}pkg-config} pkg-config; do
- AC_CHECK_PROG(PKG_CONFIG, $prog, [$prog], [], [],
- [`"$as_dir/$ac_word$ac_exec_ext" --print-errors --version > /dev/null 2>&1 || echo "$as_dir/$ac_word$ac_exec_ext"`])
- test -z "${PKG_CONFIG}" || break
-done
+tool_warned=$ac_tool_warned ac_tool_warned=no
+AC_CHECK_TOOL(PKG_CONFIG, pkg-config)
+ac_tool_warned=$tool_warned
+AS_IF([test -z "$PKG_CONFIG"], [],
+["$PKG_CONFIG" --print-errors --version > /dev/null 2>&1], [],
+[
+ unset ac_cv_prog_PKG_CONFIG
+ PKG_CONFIG=
+ AC_MSG_WARN([$PKG_CONFIG does not work; ignore])
+])
AC_MSG_CHECKING([whether it is Android])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -599,22 +625,39 @@ RUBY_WERROR_FLAG([
cd .. && rm -fr tmp.$$.try_link
])
-: ${RPATHFLAG=''}
-rpathflag=''
-AS_IF([test x"${RPATHFLAG}" = x], [
- AS_CASE(["$target_os"],
+: "rpath" && {
+ AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
+ AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"],
+ [.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])],
+ [rb_cv_binary_elf=no])])
+
+ rpathflag=''
+ AS_IF([test x"${RPATHFLAG=}" = x], [
+ AS_CASE(["$target_os"],
[aix*], [rpathflag='-blibpath:'],
- [for rpathflag in -R "-rpath "; do
+ [for rpathflag in "-rpath " -R; do
AS_CASE("$rpathflag",
[*" "], [AS_CASE(["${linker_flag}"],
[*,], [rpathflag=`echo "$rpathflag" | tr ' ' ,`])])
rpathflag="${linker_flag}${rpathflag}"
RUBY_TRY_LDFLAGS([${rpathflag}.], [], [rpathflag=])
- AS_IF([test "x${rpathflag}" != x], [])
+ AS_IF([test "x${rpathflag}" != x], [break])
done])
-], [
- rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'`
-])
+ ], [
+ rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'`
+ ])
+
+ AC_ARG_ENABLE(rpath,
+ AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries.
+ enabled by default on ELF platforms]),
+ [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"])
+
+ AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [
+ RPATHFLAG="${rpathflag:+ ${rpathflag}%1\$-s}"
+ ])
+ AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
+}
RUBY_TRY_LDFLAGS(-fdeclspec, [fdeclspec=yes], [fdeclspec=no])
AS_IF([test "$fdeclspec" = yes], [
@@ -757,7 +800,8 @@ AS_IF([test "$GCC" = yes], [
[disable -D_FORTIFY_SOURCE=2 option, which causes link error on mingw]),
[fortify_source=$enableval])
AS_IF([test "x$fortify_source" != xno], [
- RUBY_TRY_CFLAGS([$optflags -D_FORTIFY_SOURCE=2], [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)], [],
+ RUBY_TRY_CFLAGS([$optflags -D_FORTIFY_SOURCE=2],
+ [RUBY_APPEND_OPTION(XCFLAGS, -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2)], [],
[@%:@include <stdio.h>])
])
@@ -765,7 +809,7 @@ AS_IF([test "$GCC" = yes], [
# -fstack-protector
AS_CASE(["$target_os"],
- [mingw*|emscripten*|wasi*], [
+ [emscripten*|wasi*], [
stack_protector=no
])
AS_IF([test -z "${stack_protector+set}"], [
@@ -777,6 +821,8 @@ AS_IF([test "$GCC" = yes], [
AS_IF([test "x$stack_protector" = xyes], [stack_protector=option; break])
])
])
+ AC_MSG_CHECKING([for -fstack-protector])
+ AC_MSG_RESULT(["$stack_protector"])
AS_CASE(["$stack_protector"], [-*], [
RUBY_APPEND_OPTION(XCFLAGS, $stack_protector)
RUBY_APPEND_OPTION(XLDFLAGS, $stack_protector)
@@ -842,33 +888,6 @@ AS_IF([test "$GCC" = yes], [
# need lgamma_r()
])
- # ANSI (no XCFLAGS because this is C only)
- AS_CASE(["$target_os"],
- [solaris*], [
- # Because "-std=gnu99" affects existence of functions on Solaris,
- # "-std=gnu99" will be appended to CPPFLAGS.
- for ansi_options in -std=gnu99; do
- RUBY_TRY_CFLAGS(${ansi_options}, [
- RUBY_APPEND_OPTIONS(CPPFLAGS, ${ansi_options})
- ], [ansi_options=])
- test "x${ansi_options}" = x || break
- done
- ],
- [
- # ANSI (no XCFLAGS because this is C only)
- rb_tmp_std_check=`echo $CC $CFLAGS $optflags $warnflags $debugflags | fgrep std= | tr -d '\015'`
- AS_IF([test "x$rb_tmp_std_check" = "x"],
- [
- for ansi_options in -std=gnu99; do
- RUBY_TRY_CFLAGS(${ansi_options}, [
- RUBY_APPEND_OPTIONS(warnflags, ${ansi_options})
- RUBY_APPEND_OPTIONS(strict_warnflags, ${ansi_options})
- ], [ansi_options=])
- test "x${ansi_options}" = x || break
- done
- ])
- ])
-
# suppress annoying -Wstrict-overflow warnings
RUBY_TRY_CFLAGS(-fno-strict-overflow, [RUBY_APPEND_OPTION(XCFLAGS, -fno-strict-overflow)])
@@ -931,17 +950,35 @@ AS_CASE(["$target_cpu"], [[i[3-6]86*]], [
AS_IF([test "$rb_cv_gcc_compiler_cas" = i486], [ARCH_FLAG="-march=i486"])
])
+OPT_DIR=
+AC_ARG_WITH([gmp-dir],
+ AS_HELP_STRING([--with-gmp-dir=DIR],
+ [specify the prefix directory where gmp is installed]),
+ [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], [])
+AC_ARG_WITH([gmp],
+ [AS_HELP_STRING([--without-gmp],
+ [disable GNU GMP to accelerate Bignum operations])],
+ [], [with_gmp=yes])
+
AC_ARG_WITH(opt-dir,
AS_HELP_STRING([--with-opt-dir=DIR-LIST],
[add optional headers and libraries directories separated by $PATH_SEPARATOR]),
- [
- val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -I\1/include|g;s/^ //"`
- CPPFLAGS="$CPPFLAGS $val"
- val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -L\1/lib${rpathflag:+ $rpathflag\\\\1/lib}|g;s/^ //"`
- LDFLAGS="$LDFLAGS $val"
- LDFLAGS_OPTDIR="$val"
- OPT_DIR="$withval"
- ], [OPT_DIR=])
+ [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], [])
+
+AS_IF([test "x$OPT_DIR" != x], [
+ val=`IFS="$PATH_SEPARATOR"
+ for dir in $OPT_DIR; do
+ test -z "$dir" && continue
+ echo x ${LIBPATHFLAG} ${RPATHFLAG} |
+ sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g"
+ done | tr '\012' ' ' | sed 's/ *$//'`
+ LDFLAGS="${LDFLAGS:+$LDFLAGS }$val"
+ DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val"
+ LDFLAGS_OPTDIR="$val"
+ INCFLAGS="${INCFLAGS:+$INCFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' |
+ sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'`
+])
+AC_SUBST(incflags, "$INCFLAGS")
test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\""
test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\""
@@ -1160,15 +1197,12 @@ main()
ac_cv_func_getpgrp_void=no
ac_cv_func_memcmp_working=yes
ac_cv_lib_dl_dlopen=no
- rb_cv_binary_elf=no
rb_cv_negative_time_t=yes
ac_cv_func_fcntl=yes
ac_cv_func_flock=yes
ac_cv_func_gmtime_r=yes
rb_cv_large_fd_select=yes
ac_cv_type_struct_timeval=yes
- ac_cv_func_clock_gettime=yes
- ac_cv_func_clock_getres=yes
ac_cv_func_malloc_usable_size=no
ac_cv_type_off_t=yes
ac_cv_sizeof_off_t=8
@@ -1263,6 +1297,11 @@ dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS
AC_HEADER_STDBOOL
AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([afunix.h], [], [],
+[#ifdef _WIN32
+# include <winsock2.h>
+#endif
+])
AC_CHECK_HEADERS(atomic.h)
AC_CHECK_HEADERS(copyfile.h)
AC_CHECK_HEADERS(direct.h)
@@ -1309,16 +1348,13 @@ AC_CHECK_HEADERS(syscall.h)
AC_CHECK_HEADERS(time.h)
AC_CHECK_HEADERS(ucontext.h)
AC_CHECK_HEADERS(utime.h)
+AC_CHECK_HEADERS(stdatomic.h)
+
AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [
AC_CHECK_HEADERS(x86intrin.h)
])
RUBY_UNIVERSAL_CHECK_HEADER([x86_64, i386], x86intrin.h)
-AC_ARG_WITH([gmp],
- [AS_HELP_STRING([--without-gmp],
- [disable GNU GMP to accelerate Bignum operations])],
- [],
- [with_gmp=yes])
AS_IF([test "x$with_gmp" != xno],
[AC_CHECK_HEADERS(gmp.h)
AS_IF([test "x$ac_cv_header_gmp_h" != xno],
@@ -1330,6 +1366,8 @@ AC_ARG_WITH([jemalloc],
[with_jemalloc=$withval], [with_jemalloc=no])
AS_IF([test "x$with_jemalloc" != xno],[
# find jemalloc header first
+ save_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${INCFLAGS} ${CPPFLAGS}"
malloc_header=
AC_CHECK_HEADER(jemalloc/jemalloc.h, [malloc_header=jemalloc/jemalloc.h], [
AC_CHECK_HEADER(jemalloc.h, [malloc_header=jemalloc.h])
@@ -1361,6 +1399,8 @@ AS_IF([test "x$with_jemalloc" != xno],[
done
done
])
+ CPPFLAGS="${save_CPPFLAGS}"
+ unset save_CPPFLAGS
with_jemalloc=${rb_cv_jemalloc_library}
AS_CASE(["$with_jemalloc"],
[no],
@@ -1890,8 +1930,8 @@ AS_CASE(["${target_cpu}-${target_os}:${target_archs}"],
[universal-darwin*:*ppc*], [
AC_LIBSOURCES(alloca.c)
AC_SUBST([ALLOCA], [\${LIBOBJDIR}alloca.${ac_objext}])
- RUBY_DEFINE_IF([defined __powerpc__], C_ALLOCA, 1)
- RUBY_DEFINE_IF([defined __powerpc__], alloca, alloca)
+ RUBY_DEFINE_IF([defined __POWERPC__], C_ALLOCA, 1) # Darwin defines __POWERPC__ for ppc and ppc64 both
+ RUBY_DEFINE_IF([defined __POWERPC__], alloca, alloca)
],
[
AC_FUNC_ALLOCA
@@ -1971,6 +2011,7 @@ AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type)
test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no
AC_CHECK_FUNCS(arc4random_buf)
AC_CHECK_FUNCS(atan2l atan2f)
+AC_CHECK_DECLS(atomic_signal_fence, [], [], [#include <stdatomic.h>])
AC_CHECK_FUNCS(chmod)
AC_CHECK_FUNCS(chown)
AC_CHECK_FUNCS(chroot)
@@ -2561,10 +2602,13 @@ AS_CASE([$coroutine_type], [yes|''], [
[arm64-darwin*], [
coroutine_type=arm64
],
- [powerpc-darwin*], [
+ # Correct target name is powerpc*-, but Ruby seems to prefer ppc*-.
+ # Notice that Darwin PPC ABI differs from AIX and ELF.
+ # Adding PPC targets for AIX, *BSD and *Linux will require separate implementations.
+ [powerpc-darwin*|ppc-darwin*], [
coroutine_type=ppc
],
- [powerpc64-darwin*], [
+ [powerpc64-darwin*|ppc64-darwin*], [
coroutine_type=ppc64
],
[x*64-linux*], [
@@ -2870,12 +2914,6 @@ AC_ARG_WITH(dln-a-out,
])
])
-AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
-AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"],
-[.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])],
-rb_cv_binary_elf=no)])
-
AS_IF([test "$rb_cv_binary_elf" = yes], [
AC_DEFINE(USE_ELF)
AC_CHECK_HEADERS([elf.h elf_abi.h])
@@ -2952,13 +2990,25 @@ STATIC=
])
}
-: "rpath" && {
- AC_ARG_ENABLE(rpath,
- AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries.
- enabled by default on ELF platforms]),
- [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"])
+EXTSTATIC=
+AC_SUBST(EXTSTATIC)dnl
+AC_ARG_WITH(static-linked-ext,
+ AS_HELP_STRING([--with-static-linked-ext], [link external modules statically]),
+ [AS_CASE([$withval],[yes],[STATIC=;EXTSTATIC=static],[no],[],[EXTSTATIC="$withval"])])
+AS_CASE([",$EXTSTATIC,"], [,static,|*,enc,*], [
+ ENCOBJS='enc/encinit.$(OBJEXT) enc/libenc.$(LIBEXT) enc/libtrans.$(LIBEXT)'
+ EXTOBJS='ext/extinit.$(OBJEXT)'
+ AC_DEFINE_UNQUOTED(EXTSTATIC, 1)
+ AC_SUBST(ENCSTATIC, static)
+], [
+ ENCOBJS='dmyenc.$(OBJEXT)'
+ EXTOBJS='dmyext.$(OBJEXT)'
+])
+AC_SUBST(ENCOBJS)
+AC_SUBST(EXTOBJS)
- AS_CASE(["$target_os"],
+: "rpath" && {
+ AS_CASE(["$target_os"],
[solaris*], [ AS_IF([test "$GCC" = yes], [
: ${LDSHARED='$(CC) -shared'}
AS_IF([test "$rb_cv_prog_gnu_ld" = yes], [
@@ -2998,7 +3048,6 @@ STATIC=
rb_cv_dlopen=yes],
[interix*], [ : ${LDSHARED='$(CC) -shared'}
XLDFLAGS="$XLDFLAGS -Wl,-E"
- LIBPATHFLAG=" -L%1\$-s"
rb_cv_dlopen=yes],
[freebsd*|dragonfly*], [
: ${LDSHARED='$(CC) -shared'}
@@ -3017,7 +3066,7 @@ STATIC=
[darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'}
: ${DLDSHARED='$(CC) -dynamiclib'}
: ${LDFLAGS=""}
- : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH}
+ : ${LIBPATHENV=DYLD_LIBRARY_PATH}
: ${PRELOADENV=DYLD_INSERT_LIBRARIES}
AS_IF([test x"$enable_shared" = xyes], [
# Resolve symbols from libruby.dylib when --enable-shared
@@ -3058,30 +3107,35 @@ STATIC=
[atheos*], [ : ${LDSHARED='$(CC) -shared'}
rb_cv_dlopen=yes],
[ : ${LDSHARED='$(LD)'}])
- AC_MSG_RESULT($rb_cv_dlopen)
+ AC_MSG_RESULT($rb_cv_dlopen)
+}
- AS_IF([test "$rb_cv_dlopen" = yes], [
+AS_IF([test "$rb_cv_dlopen" = yes], [
AS_CASE(["$target_os"],
- [darwin*], [
+ [darwin*], [
+ AC_SUBST(ADDITIONAL_DLDFLAGS, "")
for flag in \
- "-undefined dynamic_lookup" \
"-multiply_defined suppress" \
+ "-undefined dynamic_lookup" \
; do
- test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
- RUBY_TRY_LDFLAGS([$flag], [], [flag=])
- AS_IF([test "x$flag" != x], [
- RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
- ])
+ test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
+ RUBY_TRY_LDFLAGS([$flag], [], [flag=])
+ AS_IF([test x"$flag" = x], [continue])
+
+ AC_MSG_CHECKING([whether $flag is accepted for bundle])
+ : > conftest.c
+ AS_IF([${LDSHARED%%'$(CC)'*}$CC${LDSHARED@%:@*'$(CC)'} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err &&
+ test ! -s conftest.err], [
+ AC_MSG_RESULT([yes])
+ RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
+ ], [
+ AC_MSG_RESULT([no])
+ RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag])
+ ])
+ rm -fr conftest.*
done
- ])
- ])
-
- AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [
- AS_IF([test "x$rpathflag" != x], [
- RPATHFLAG=" ${rpathflag}%1\$-s"
- ])
- ])
-}
+ ])
+])
AS_IF([test "${LDSHAREDXX}" = ""], [
AS_CASE(["${LDSHARED}"],
@@ -3097,7 +3151,6 @@ AS_IF([test "${LDSHAREDXX}" = ""], [
[ld" "*], [
])
])
-AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
AC_SUBST(LINK_SO)
AC_SUBST(LIBPATHFLAG)
@@ -3106,23 +3159,6 @@ AC_SUBST(LIBPATHENV, "${LIBPATHENV-LD_LIBRARY_PATH}")
AC_SUBST(PRELOADENV, "${PRELOADENV-LD_PRELOAD}")
AC_SUBST(TRY_LINK)
-AS_IF([test "x$OPT_DIR" != x], [
- pat=`echo "${LDFLAGS_OPTDIR}" | sed ['s/[][\\.*|]/\\\\&/']`
- LDFLAGS=`echo "${LDFLAGS}" | sed "s| ${pat}||"`
- val=`IFS="$PATH_SEPARATOR"
- for dir in $OPT_DIR; do
- echo x ${LIBPATHFLAG} ${RPATHFLAG} |
- sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g"
- done | tr '\012' ' ' | sed 's/ *$//'`
- AS_IF([test x"$val" != x], [
- test x"${LDFLAGS}" = x || LDFLAGS="$LDFLAGS "
- LDFLAGS="$LDFLAGS$val"
- test x"${DLDFLAGS}" = x || DLDFLAGS="$DLDFLAGS "
- DLDFLAGS="$DLDFLAGS$val"
- ])
- LDFLAGS_OPTDIR="$val"
-])
-
AS_CASE(["$target_os"],
[freebsd*], [
AC_CHECK_LIB([procstat], [procstat_open_sysctl])
@@ -3275,23 +3311,6 @@ AC_ARG_WITH(ext,
AC_ARG_WITH(out-ext,
AS_HELP_STRING([--with-out-ext=EXTS],
[pass to --without-ext option of extmk.rb]))
-EXTSTATIC=
-AC_SUBST(EXTSTATIC)dnl
-AC_ARG_WITH(static-linked-ext,
- AS_HELP_STRING([--with-static-linked-ext], [link external modules statically]),
- [AS_CASE([$withval],[yes],[STATIC=;EXTSTATIC=static],[no],[],[EXTSTATIC="$withval"])])
-AS_CASE([",$EXTSTATIC,"], [,static,|*,enc,*], [
- ENCOBJS='enc/encinit.$(OBJEXT) enc/libenc.$(LIBEXT) enc/libtrans.$(LIBEXT)'
- EXTOBJS='ext/extinit.$(OBJEXT)'
- AC_DEFINE_UNQUOTED(EXTSTATIC, 1)
- AC_SUBST(ENCSTATIC, static)
-], [
- ENCOBJS='dmyenc.$(OBJEXT)'
- EXTOBJS='dmyext.$(OBJEXT)'
-])
-AC_SUBST(ENCOBJS)
-AC_SUBST(EXTOBJS)
-
AC_ARG_WITH(setup,
AS_HELP_STRING([--with-setup=SETUP], [use extension libraries setup]),
[setup=$withval])
@@ -3341,7 +3360,6 @@ for var in bindir includedir libdir rubylibprefix; do
done
BTESTRUBY='$(MINIRUBY)'
-BOOTSTRAPRUBY='$(BASERUBY)'
AS_IF([test x"$cross_compiling" = xyes], [
test x"$MINIRUBY" = x && MINIRUBY="${RUBY-$BASERUBY} -I`$CHDIR .; pwd` "-r'$(arch)-fake'
XRUBY_LIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["libdir"]']`
@@ -3351,6 +3369,10 @@ AS_IF([test x"$cross_compiling" = xyes], [
AC_SUBST(XRUBY_RUBYLIBDIR)
AC_SUBST(XRUBY_RUBYHDRDIR)
PREP='$(arch)-fake.rb'
+ AS_CASE(["$enable_shared:$EXTSTATIC:$target_os"], [no::darwin*], [
+ # darwin target requires miniruby for linking ext bundles
+ PREP="$PREP"' miniruby$(EXEEXT)'
+ ])
RUNRUBY_COMMAND='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`'
RUNRUBY='$(RUNRUBY_COMMAND)'
XRUBY='$(MINIRUBY)'
@@ -3364,7 +3386,6 @@ AS_IF([test x"$cross_compiling" = xyes], [
RUNRUBY_COMMAND='$(MINIRUBY) $(tooldir)/runruby.rb --extout=$(EXTOUT) $(RUNRUBYOPT)'
RUNRUBY='$(RUNRUBY_COMMAND) --'
XRUBY='$(RUNRUBY)'
- AS_CASE(["$HAVE_BASERUBY:$build_os"], [no:*|*:mingw*], [BOOTSTRAPRUBY='$(MINIRUBY)'])
TEST_RUNNABLE=yes
CROSS_COMPILING=no
])
@@ -3376,7 +3397,6 @@ AC_SUBST(PREP)
AC_SUBST(RUNRUBY_COMMAND)
AC_SUBST(RUNRUBY)
AC_SUBST(XRUBY)
-AC_SUBST(BOOTSTRAPRUBY)
AC_SUBST(EXTOUT, [${EXTOUT=.ext}])
FIRSTMAKEFILE=""
@@ -3615,7 +3635,7 @@ AS_CASE("$cross_compiling:${LIBPATHENV}", [yes:* | no:], [], [
AC_MSG_CHECKING(whether wrapper for $LIBPATHENV is needed)
AS_IF([env ${LIBPATHENV}=/lib /bin/sh -c ': ${'${LIBPATHENV}'?}' 2>/dev/null],
[AC_MSG_RESULT(no)],
- [PREP="$PREP"' exe/$(PROGRAM)'
+ [AC_SUBST(XRUBY_LIBPATHENV_WRAPPER, 'exe/$(PROGRAM)')
AC_MSG_RESULT(yes)]
)
])
@@ -3733,7 +3753,7 @@ AC_ARG_ENABLE(jit-support,
AS_HELP_STRING([--disable-jit-support], [disable JIT features]),
[MJIT_SUPPORT=$enableval],
[AS_CASE(["$target_os"],
- [wasi | mingw*], [MJIT_SUPPORT=no],
+ [wasi | mingw* | solaris*], [MJIT_SUPPORT=no],
[MJIT_SUPPORT=yes]
)])
@@ -3743,10 +3763,58 @@ AS_IF([test x"$MJIT_SUPPORT" = "xyes"],
AC_SUBST(MJIT_SUPPORT)
+AC_CHECK_PROG(RUSTC, [rustc], [rustc], [no]) dnl no ac_tool_prefix
+
+dnl check if rustc is recent enough to build YJIT (rustc >= 1.58.0)
+YJIT_RUSTC_OK=no
+AS_IF([test "$RUSTC" != "no"],
+ AC_MSG_CHECKING([whether ${RUSTC} works for YJIT])
+ YJIT_TARGET_ARCH=
+ AS_CASE(["$target_cpu"],
+ [arm64|aarch64], [YJIT_TARGET_ARCH=aarch64],
+ [x86_64], [YJIT_TARGET_ARCH=x86_64],
+ )
+ dnl Fails in case rustc target doesn't match ruby target.
+ dnl Can happen on Rosetta, for example.
+ AS_IF([echo "#[cfg(target_arch = \"$YJIT_TARGET_ARCH\")] fn main() { let x = 1; format!(\"{x}\"); }" |
+ $RUSTC - --emit asm=/dev/null 2>/dev/null],
+ [YJIT_RUSTC_OK=yes]
+ )
+ AC_MSG_RESULT($YJIT_RUSTC_OK)
+)
+
+dnl check if we can build YJIT on this target platform
+dnl we can't easily cross-compile with rustc so we don't support that
+YJIT_TARGET_OK=no
+AS_IF([test "$cross_compiling" = no],
+ AS_CASE(["$target_cpu-$target_os"],
+ [*android*], [
+ YJIT_TARGET_OK=no
+ ],
+ [arm64-darwin*|aarch64-darwin*|x86_64-darwin*], [
+ YJIT_TARGET_OK=yes
+ ],
+ [arm64-*linux*|aarch64-*linux*|x86_64-*linux*], [
+ YJIT_TARGET_OK=yes
+ ],
+ [arm64-*bsd*|aarch64-*bsd*|x86_64-*bsd*], [
+ YJIT_TARGET_OK=yes
+ ]
+ )
+)
+
+dnl build YJIT in release mode if rustc >= 1.58.0 is present and we are on a supported platform
AC_ARG_ENABLE(yjit,
- AS_HELP_STRING([--enable-yjit],
- [enable experimental in-process JIT compiler that requires Rust build tools [default=no]]),
- [YJIT_SUPPORT=$enableval], [YJIT_SUPPORT=no])
+ AS_HELP_STRING([--enable-yjit],
+ [enable in-process JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.58.0+ is available]),
+ [YJIT_SUPPORT=$enableval],
+ [AS_CASE(["$enable_jit_support:$YJIT_TARGET_OK:$YJIT_RUSTC_OK"],
+ [yes:yes:yes|:yes:yes], [
+ YJIT_SUPPORT=yes
+ ],
+ [YJIT_SUPPORT=no]
+ )]
+)
CARGO=
CARGO_BUILD_ARGS=
@@ -3756,7 +3824,6 @@ AS_CASE(["${YJIT_SUPPORT}"],
AS_IF([test x"$enable_jit_support" = "xno"],
AC_MSG_ERROR([--disable-jit-support but --enable-yjit. YJIT requires JIT support])
)
- AC_CHECK_TOOL(RUSTC, [rustc], [no])
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
@@ -3767,16 +3834,17 @@ AS_CASE(["${YJIT_SUPPORT}"],
],
[dev], [
rb_rust_target_subdir=debug
- CARGO_BUILD_ARGS='--features stats,disasm,asm_comments'
+ CARGO_BUILD_ARGS='--features stats,disasm'
AC_DEFINE(RUBY_DEBUG, 1)
],
[dev_nodebug], [
rb_rust_target_subdir=dev_nodebug
- CARGO_BUILD_ARGS='--profile dev_nodebug --features stats,disasm,asm_comments'
+ CARGO_BUILD_ARGS='--profile dev_nodebug --features stats,disasm'
],
[stats], [
rb_rust_target_subdir=stats
CARGO_BUILD_ARGS='--profile stats --features stats'
+ AC_DEFINE(YJIT_STATS, 1)
])
AS_IF([test -n "${CARGO_BUILD_ARGS}"], [
@@ -3786,11 +3854,17 @@ AS_CASE(["${YJIT_SUPPORT}"],
]))
YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a"
+ AS_CASE(["$target_os"],[openbsd*],[
+ # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
+ LDFLAGS="$LDFLAGS -lpthread -lc++abi"
+ ])
YJIT_OBJ='yjit.$(OBJEXT)'
+ AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [
+ AC_DEFINE_UNQUOTED(YJIT_SUPPORT, [$YJIT_SUPPORT])
+ ])
AC_DEFINE(USE_YJIT, 1)
], [AC_DEFINE(USE_YJIT, 0)])
-
dnl These variables end up in ::RbConfig::CONFIG
AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
AC_SUBST(RUSTC)dnl Rust compiler command
@@ -4362,6 +4436,14 @@ AC_SUBST(XCC_WRAPPER)
AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`])
+AS_IF([test ! -f "$srcdir/revision.h"], [
+ AS_IF([test "x$HAVE_BASERUBY" = xyes], [
+ ${BASERUBY} -C "$srcdir" tool/file2lastrev.rb -q --revision.h > "$srcdir/revision.h"
+ ], [
+ touch "$srcdir/revision.h"
+ ])
+])
+
AS_IF([test x"$firstmf" != x], [
AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"])
])
@@ -4378,17 +4460,24 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [
AS_CASE("$VCS",
['$(GIT)'|git], [VCSUP='$(VCS) pull --rebase $(GITPULLOPTIONS)'],
[VCSUP='$(VCS)'])
- sed -n \
- -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \
- -e 's//\1 = \2/' \
- -e '[s/ \([0-9]\)$/ 0\1/]' \
- -e p \
- -e '}' "$srcdir/version.h"
+ for f in "$srcdir/version.h" "$srcdir/revision.h"; do
+ test -f "$f" || continue
+ sed -n \
+ -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \
+ -e 's//\1 = \2/' \
+ -e '[s/ \([0-9]\)$/ 0\1/]' \
+ -e p \
+ -e '}' "$f"
+ done
sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile
echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)'
AS_IF([test "$gnumake" != yes], [
echo ['$(MKFILES): $(srcdir)/common.mk']
sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk
+ AS_IF([test "$YJIT_SUPPORT" = yes], [
+ cat ${srcdir}/yjit/not_gmake.mk
+ echo ['$(MKFILES): ${srcdir}/yjit/not_gmake.mk']
+ ])
], [
echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk'
])
@@ -4406,7 +4495,7 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [
echo 'ruby: $(PROGRAM);' >> $tmpmk
test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk"
]) && mv -f $tmpmk Makefile],
-[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT'])
+[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT' YJIT_SUPPORT='$YJIT_SUPPORT'])
AC_ARG_WITH([ruby-pc],
AS_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
diff --git a/cont.c b/cont.c
index 8a9aded713..5375d1945b 100644
--- a/cont.c
+++ b/cont.c
@@ -29,11 +29,15 @@ extern int madvise(caddr_t, size_t, int);
#include "gc.h"
#include "internal.h"
#include "internal/cont.h"
+#include "internal/error.h"
#include "internal/proc.h"
+#include "internal/sanitizers.h"
#include "internal/warnings.h"
#include "ruby/fiber/scheduler.h"
#include "mjit.h"
+#include "yjit.h"
#include "vm_core.h"
+#include "vm_sync.h"
#include "id_table.h"
#include "ractor_core.h"
@@ -66,6 +70,8 @@ static VALUE rb_cFiberPool;
#define FIBER_POOL_ALLOCATION_FREE
#endif
+#define jit_cont_enabled (mjit_enabled || rb_yjit_enabled_p())
+
enum context_type {
CONTINUATION_CONTEXT = 0,
FIBER_CONTEXT = 1
@@ -194,6 +200,15 @@ struct fiber_pool {
size_t vm_stack_size;
};
+// Continuation contexts used by JITs
+struct rb_jit_cont {
+ rb_execution_context_t *ec; // continuation ec
+ struct rb_jit_cont *prev, *next; // used to form lists
+};
+
+// Doubly linked list for enumerating all on-stack ISEQs.
+static struct rb_jit_cont *first_jit_cont;
+
typedef struct rb_context_struct {
enum context_type type;
int argc;
@@ -211,8 +226,7 @@ typedef struct rb_context_struct {
rb_execution_context_t saved_ec;
rb_jmpbuf_t jmpbuf;
rb_ensure_entry_t *ensure_array;
- /* Pointer to MJIT info about the continuation. */
- struct mjit_cont *mjit_cont;
+ struct rb_jit_cont *jit_cont; // Continuation contexts for JITs
} rb_context_t;
@@ -258,7 +272,7 @@ struct rb_fiber_struct {
static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
-static ID fiber_initialize_keywords[2] = {0};
+static ID fiber_initialize_keywords[3] = {0};
/*
* FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
@@ -680,21 +694,36 @@ fiber_pool_stack_free(struct fiber_pool_stack * stack)
if (DEBUG) fprintf(stderr, "fiber_pool_stack_free: %p+%"PRIuSIZE" [base=%p, size=%"PRIuSIZE"]\n", base, size, stack->base, stack->size);
-#if VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
+ // The pages being used by the stack can be returned back to the system.
+ // That doesn't change the page mapping, but it does allow the system to
+ // reclaim the physical memory.
+ // Since we no longer care about the data itself, we don't need to page
+ // out to disk, since that is costly. Not all systems support that, so
+ // we try our best to select the most efficient implementation.
+ // In addition, it's actually slightly desirable to not do anything here,
+ // but that results in higher memory usage.
+
+#ifdef __wasi__
+ // WebAssembly doesn't support madvise, so we just don't do anything.
+#elif VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
// This immediately discards the pages and the memory is reset to zero.
madvise(base, size, MADV_DONTNEED);
-#elif defined(POSIX_MADV_DONTNEED)
- posix_madvise(base, size, POSIX_MADV_DONTNEED);
#elif defined(MADV_FREE_REUSABLE)
+ // Darwin / macOS / iOS.
// Acknowledge the kernel down to the task info api we make this
// page reusable for future use.
// As for MADV_FREE_REUSE below we ensure in the rare occasions the task was not
// completed at the time of the call to re-iterate.
while (madvise(base, size, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN);
#elif defined(MADV_FREE)
+ // Recent Linux.
madvise(base, size, MADV_FREE);
#elif defined(MADV_DONTNEED)
+ // Old Linux.
madvise(base, size, MADV_DONTNEED);
+#elif defined(POSIX_MADV_DONTNEED)
+ // Solaris?
+ posix_madvise(base, size, POSIX_MADV_DONTNEED);
#elif defined(_WIN32)
VirtualAlloc(base, size, MEM_RESET, PAGE_READWRITE);
// Not available in all versions of Windows.
@@ -999,6 +1028,8 @@ fiber_is_root_p(const rb_fiber_t *fiber)
}
#endif
+static void jit_cont_free(struct rb_jit_cont *cont);
+
static void
cont_free(void *ptr)
{
@@ -1019,9 +1050,9 @@ cont_free(void *ptr)
RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
- if (mjit_enabled) {
- VM_ASSERT(cont->mjit_cont != NULL);
- mjit_cont_free(cont->mjit_cont);
+ if (jit_cont_enabled) {
+ VM_ASSERT(cont->jit_cont != NULL);
+ jit_cont_free(cont->jit_cont);
}
/* free rb_cont_t or rb_fiber_t */
ruby_xfree(ptr);
@@ -1126,7 +1157,9 @@ fiber_memsize(const void *ptr)
*/
if (saved_ec->local_storage && fiber != th->root_fiber) {
size += rb_id_table_memsize(saved_ec->local_storage);
+ size += rb_obj_memsize_of(saved_ec->storage);
}
+
size += cont_memsize(&fiber->cont);
return size;
}
@@ -1161,6 +1194,7 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
}
FLUSH_REGISTER_WINDOWS;
+ asan_unpoison_memory_region(cont->machine.stack_src, size, false);
MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size);
}
@@ -1185,12 +1219,103 @@ cont_save_thread(rb_context_t *cont, rb_thread_t *th)
sec->machine.stack_end = NULL;
}
+static rb_nativethread_lock_t jit_cont_lock;
+
+// Register a new continuation with execution context `ec`. Return JIT info about
+// the continuation.
+static struct rb_jit_cont *
+jit_cont_new(rb_execution_context_t *ec)
+{
+ struct rb_jit_cont *cont;
+
+ // We need to use calloc instead of something like ZALLOC to avoid triggering GC here.
+ // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup,
+ // the thread is still being prepared and marking it causes SEGV.
+ cont = calloc(1, sizeof(struct rb_jit_cont));
+ if (cont == NULL)
+ rb_memerror();
+ cont->ec = ec;
+
+ rb_native_mutex_lock(&jit_cont_lock);
+ if (first_jit_cont == NULL) {
+ cont->next = cont->prev = NULL;
+ }
+ else {
+ cont->prev = NULL;
+ cont->next = first_jit_cont;
+ first_jit_cont->prev = cont;
+ }
+ first_jit_cont = cont;
+ rb_native_mutex_unlock(&jit_cont_lock);
+
+ return cont;
+}
+
+// Unregister continuation `cont`.
+static void
+jit_cont_free(struct rb_jit_cont *cont)
+{
+ if (!cont) return;
+
+ rb_native_mutex_lock(&jit_cont_lock);
+ if (cont == first_jit_cont) {
+ first_jit_cont = cont->next;
+ if (first_jit_cont != NULL)
+ first_jit_cont->prev = NULL;
+ }
+ else {
+ cont->prev->next = cont->next;
+ if (cont->next != NULL)
+ cont->next->prev = cont->prev;
+ }
+ rb_native_mutex_unlock(&jit_cont_lock);
+
+ free(cont);
+}
+
+// Call a given callback against all on-stack ISEQs.
+void
+rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data)
+{
+ struct rb_jit_cont *cont;
+ for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
+ if (cont->ec->vm_stack == NULL)
+ continue;
+
+ const rb_control_frame_t *cfp;
+ for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
+ const rb_iseq_t *iseq;
+ if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((VALUE)iseq) == imemo_iseq) {
+ callback(iseq, data);
+ }
+
+ if (cfp == cont->ec->cfp)
+ break; // reached the most recent cfp
+ }
+ }
+}
+
+// Finish working with jit_cont.
+void
+rb_jit_cont_finish(void)
+{
+ if (!jit_cont_enabled)
+ return;
+
+ struct rb_jit_cont *cont, *next;
+ for (cont = first_jit_cont; cont != NULL; cont = next) {
+ next = cont->next;
+ free(cont); // Don't use xfree because it's allocated by calloc.
+ }
+ rb_native_mutex_destroy(&jit_cont_lock);
+}
+
static void
-cont_init_mjit_cont(rb_context_t *cont)
+cont_init_jit_cont(rb_context_t *cont)
{
- VM_ASSERT(cont->mjit_cont == NULL);
- if (mjit_enabled) {
- cont->mjit_cont = mjit_cont_new(&(cont->saved_ec));
+ VM_ASSERT(cont->jit_cont == NULL);
+ if (jit_cont_enabled) {
+ cont->jit_cont = jit_cont_new(&(cont->saved_ec));
}
}
@@ -1209,7 +1334,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th)
cont->saved_ec.local_storage = NULL;
cont->saved_ec.local_storage_recursive_hash = Qnil;
cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil;
- cont_init_mjit_cont(cont);
+ cont_init_jit_cont(cont);
}
static rb_context_t *
@@ -1238,11 +1363,15 @@ rb_fiberptr_blocking(struct rb_fiber_struct *fiber)
return fiber->blocking;
}
-// This is used for root_fiber because other fibers call cont_init_mjit_cont through cont_new.
+// Start working with jit_cont.
void
-rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber)
+rb_jit_cont_init(void)
{
- cont_init_mjit_cont(&fiber->cont);
+ if (!jit_cont_enabled)
+ return;
+
+ rb_native_mutex_initialize(&jit_cont_lock);
+ cont_init_jit_cont(&GET_EC()->fiber_ptr->cont);
}
#if 0
@@ -1452,7 +1581,7 @@ cont_restore_1(rb_context_t *cont)
cont_restore_thread(cont);
/* restore machine stack */
-#ifdef _M_AMD64
+#if defined(_M_AMD64) && !defined(__MINGW64__)
{
/* workaround for x64 SEH */
jmp_buf buf;
@@ -1707,7 +1836,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta
/* push ensure stack */
for (j = 0; j < i; j++) {
func = lookup_rollback_func(target[i - j - 1].e_proc);
- if ((VALUE)func != Qundef) {
+ if (!UNDEF_P((VALUE)func)) {
(*func)(target[i - j - 1].data2);
}
}
@@ -1832,7 +1961,7 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
* the current thread, blocking and non-blocking fibers' behavior is identical.
*
* Ruby doesn't provide a scheduler class: it is expected to be implemented by
- * the user and correspond to Fiber::SchedulerInterface.
+ * the user and correspond to Fiber::Scheduler.
*
* There is also Fiber.schedule method, which is expected to immediately perform
* the given block in a non-blocking manner. Its actual implementation is up to
@@ -1883,11 +2012,207 @@ fiber_t_alloc(VALUE fiber_value, unsigned int blocking)
return fiber;
}
+static rb_fiber_t *
+root_fiber_alloc(rb_thread_t *th)
+{
+ VALUE fiber_value = fiber_alloc(rb_cFiber);
+ rb_fiber_t *fiber = th->ec->fiber_ptr;
+
+ VM_ASSERT(DATA_PTR(fiber_value) == NULL);
+ VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
+ VM_ASSERT(FIBER_RESUMED_P(fiber));
+
+ th->root_fiber = fiber;
+ DATA_PTR(fiber_value) = fiber;
+ fiber->cont.self = fiber_value;
+
+ coroutine_initialize_main(&fiber->context);
+
+ return fiber;
+}
+
+static inline rb_fiber_t*
+fiber_current(void)
+{
+ rb_execution_context_t *ec = GET_EC();
+ if (ec->fiber_ptr->cont.self == 0) {
+ root_fiber_alloc(rb_ec_thread_ptr(ec));
+ }
+ return ec->fiber_ptr;
+}
+
+static inline VALUE
+current_fiber_storage(void)
+{
+ rb_execution_context_t *ec = GET_EC();
+ return ec->storage;
+}
+
+static inline VALUE
+inherit_fiber_storage(void)
+{
+ return rb_obj_dup(current_fiber_storage());
+}
+
+static inline void
+fiber_storage_set(struct rb_fiber_struct *fiber, VALUE storage)
+{
+ fiber->cont.saved_ec.storage = storage;
+}
+
+static inline VALUE
+fiber_storage_get(rb_fiber_t *fiber)
+{
+ VALUE storage = fiber->cont.saved_ec.storage;
+ if (storage == Qnil) {
+ storage = rb_hash_new();
+ fiber_storage_set(fiber, storage);
+ }
+ return storage;
+}
+
+static void
+storage_access_must_be_from_same_fiber(VALUE self)
+{
+ rb_fiber_t *fiber = fiber_ptr(self);
+ rb_fiber_t *current = fiber_current();
+ if (fiber != current) {
+ rb_raise(rb_eArgError, "Fiber storage can only be accessed from the Fiber it belongs to");
+ }
+}
+
+/**
+ * call-seq: fiber.storage -> hash (dup)
+ *
+ * Returns a copy of the storage hash for the fiber. The method can only be called on the
+ * Fiber.current.
+ */
+static VALUE
+rb_fiber_storage_get(VALUE self)
+{
+ storage_access_must_be_from_same_fiber(self);
+ return rb_obj_dup(fiber_storage_get(fiber_ptr(self)));
+}
+
+static int
+fiber_storage_validate_each(VALUE key, VALUE value, VALUE _argument)
+{
+ Check_Type(key, T_SYMBOL);
+
+ return ST_CONTINUE;
+}
+
+static void
+fiber_storage_validate(VALUE value)
+{
+ // nil is an allowed value and will be lazily initialized.
+ if (value == Qnil) return;
+
+ if (!RB_TYPE_P(value, T_HASH)) {
+ rb_raise(rb_eTypeError, "storage must be a hash");
+ }
+
+ if (RB_OBJ_FROZEN(value)) {
+ rb_raise(rb_eFrozenError, "storage must not be frozen");
+ }
+
+ rb_hash_foreach(value, fiber_storage_validate_each, Qundef);
+}
+
+/**
+ * call-seq: fiber.storage = hash
+ *
+ * Sets the storage hash for the fiber. This feature is experimental
+ * and may change in the future. The method can only be called on the
+ * Fiber.current.
+ *
+ * You should be careful about using this method as you may inadvertently clear
+ * important fiber-storage state. You should mostly prefer to assign specific
+ * keys in the storage using Fiber::[]=.
+ *
+ * You can also use <tt>Fiber.new(storage: nil)</tt> to create a fiber with an empty
+ * storage.
+ *
+ * Example:
+ *
+ * while request = request_queue.pop
+ * # Reset the per-request state:
+ * Fiber.current.storage = nil
+ * handle_request(request)
+ * end
+ */
static VALUE
-fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigned int blocking)
+rb_fiber_storage_set(VALUE self, VALUE value)
{
+ if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
+ rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
+ "Fiber#storage= is experimental and may be removed in the future!");
+ }
+
+ storage_access_must_be_from_same_fiber(self);
+ fiber_storage_validate(value);
+
+ fiber_ptr(self)->cont.saved_ec.storage = rb_obj_dup(value);
+ return value;
+}
+
+/**
+ * call-seq: Fiber[key] -> value
+ *
+ * Returns the value of the fiber storage variable identified by +key+.
+ *
+ * The +key+ must be a symbol, and the value is set by Fiber#[]= or
+ * Fiber#store.
+ *
+ * See also Fiber::[]=.
+ */
+static VALUE
+rb_fiber_storage_aref(VALUE class, VALUE key)
+{
+ Check_Type(key, T_SYMBOL);
+
+ VALUE storage = fiber_storage_get(fiber_current());
+
+ if (storage == Qnil) return Qnil;
+
+ return rb_hash_aref(storage, key);
+}
+
+/**
+ * call-seq: Fiber[key] = value
+ *
+ * Assign +value+ to the fiber storage variable identified by +key+.
+ * The variable is created if it doesn't exist.
+ *
+ * +key+ must be a Symbol, otherwise a TypeError is raised.
+ *
+ * See also Fiber::[].
+ */
+static VALUE
+rb_fiber_storage_aset(VALUE class, VALUE key, VALUE value)
+{
+ Check_Type(key, T_SYMBOL);
+
+ VALUE storage = fiber_storage_get(fiber_current());
+
+ return rb_hash_aset(storage, key, value);
+}
+
+static VALUE
+fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigned int blocking, VALUE storage)
+{
+ if (storage == Qundef || storage == Qtrue) {
+ // The default, inherit storage (dup) from the current fiber:
+ storage = inherit_fiber_storage();
+ }
+ else /* nil, hash, etc. */ {
+ fiber_storage_validate(storage);
+ storage = rb_obj_dup(storage);
+ }
+
rb_fiber_t *fiber = fiber_t_alloc(self, blocking);
+ fiber->cont.saved_ec.storage = storage;
fiber->first_proc = proc;
fiber->stack.base = NULL;
fiber->stack.pool = fiber_pool;
@@ -1920,54 +2245,90 @@ rb_fiber_pool_default(VALUE pool)
return &shared_fiber_pool;
}
+VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber)
+{
+ VALUE storage = rb_obj_dup(ec->storage);
+ fiber->cont.saved_ec.storage = storage;
+ return storage;
+}
+
/* :nodoc: */
static VALUE
rb_fiber_initialize_kw(int argc, VALUE* argv, VALUE self, int kw_splat)
{
VALUE pool = Qnil;
VALUE blocking = Qfalse;
+ VALUE storage = Qundef;
if (kw_splat != RB_NO_KEYWORDS) {
VALUE options = Qnil;
- VALUE arguments[2] = {Qundef};
+ VALUE arguments[3] = {Qundef};
argc = rb_scan_args_kw(kw_splat, argc, argv, ":", &options);
- rb_get_kwargs(options, fiber_initialize_keywords, 0, 2, arguments);
+ rb_get_kwargs(options, fiber_initialize_keywords, 0, 3, arguments);
- if (arguments[0] != Qundef) {
+ if (!UNDEF_P(arguments[0])) {
blocking = arguments[0];
}
- if (arguments[1] != Qundef) {
+ if (!UNDEF_P(arguments[1])) {
pool = arguments[1];
}
+
+ storage = arguments[2];
}
- return fiber_initialize(self, rb_block_proc(), rb_fiber_pool_default(pool), RTEST(blocking));
+ return fiber_initialize(self, rb_block_proc(), rb_fiber_pool_default(pool), RTEST(blocking), storage);
}
/*
* call-seq:
- * Fiber.new(blocking: false) { |*args| ... } -> fiber
+ * Fiber.new(blocking: false, storage: true) { |*args| ... } -> fiber
*
- * Creates new Fiber. Initially, the fiber is not running and can be resumed with
- * #resume. Arguments to the first #resume call will be passed to the block:
+ * Creates new Fiber. Initially, the fiber is not running and can be resumed
+ * with #resume. Arguments to the first #resume call will be passed to the
+ * block:
*
- * f = Fiber.new do |initial|
- * current = initial
- * loop do
- * puts "current: #{current.inspect}"
- * current = Fiber.yield
- * end
- * end
- * f.resume(100) # prints: current: 100
- * f.resume(1, 2, 3) # prints: current: [1, 2, 3]
- * f.resume # prints: current: nil
- * # ... and so on ...
- *
- * If <tt>blocking: false</tt> is passed to <tt>Fiber.new</tt>, _and_ current thread
- * has a Fiber.scheduler defined, the Fiber becomes non-blocking (see "Non-blocking
- * Fibers" section in class docs).
+ * f = Fiber.new do |initial|
+ * current = initial
+ * loop do
+ * puts "current: #{current.inspect}"
+ * current = Fiber.yield
+ * end
+ * end
+ * f.resume(100) # prints: current: 100
+ * f.resume(1, 2, 3) # prints: current: [1, 2, 3]
+ * f.resume # prints: current: nil
+ * # ... and so on ...
+ *
+ * If <tt>blocking: false</tt> is passed to <tt>Fiber.new</tt>, _and_ current
+ * thread has a Fiber.scheduler defined, the Fiber becomes non-blocking (see
+ * "Non-blocking Fibers" section in class docs).
+ *
+ * If the <tt>storage</tt> is unspecified, the default is to inherit a copy of
+ * the storage from the current fiber. This is the same as specifying
+ * <tt>storage: true</tt>.
+ *
+ * Fiber[:x] = 1
+ * Fiber.new do
+ * Fiber[:x] # => 1
+ * Fiber[:x] = 2
+ * end.resume
+ * Fiber[:x] # => 1
+ *
+ * If the given <tt>storage</tt> is <tt>nil</tt>, this function will lazy
+ * initialize the internal storage, which starts as an empty hash.
+ *
+ * Fiber[:x] = "Hello World"
+ * Fiber.new(storage: nil) do
+ * Fiber[:x] # nil
+ * end
+ *
+ * Otherwise, the given <tt>storage</tt> is used as the new fiber's storage,
+ * and it must be an instance of Hash.
+ *
+ * Explicitly using <tt>storage: true</tt> is currently experimental and may
+ * change in the future.
*/
static VALUE
rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
@@ -1976,9 +2337,15 @@ rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
}
VALUE
+rb_fiber_new_storage(rb_block_call_func_t func, VALUE obj, VALUE storage)
+{
+ return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 0, storage);
+}
+
+VALUE
rb_fiber_new(rb_block_call_func_t func, VALUE obj)
{
- return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 1);
+ return rb_fiber_new_storage(func, obj, Qtrue);
}
static VALUE
@@ -1989,7 +2356,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat)
VALUE fiber = Qnil;
if (scheduler != Qnil) {
- fiber = rb_funcall_passing_block_kw(scheduler, rb_intern("fiber"), argc, argv, kw_splat);
+ fiber = rb_fiber_scheduler_fiber(scheduler, argc, argv, kw_splat);
}
else {
rb_raise(rb_eRuntimeError, "No scheduler is available!");
@@ -2032,7 +2399,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat)
*
* Note that the behavior described above is how the method is <em>expected</em>
* to behave, actual behavior is up to the current scheduler's implementation of
- * Fiber::SchedulerInterface#fiber method. Ruby doesn't enforce this method to
+ * Fiber::Scheduler#fiber method. Ruby doesn't enforce this method to
* behave in any particular way.
*
* If the scheduler is not set, the method raises
@@ -2051,7 +2418,7 @@ rb_fiber_s_schedule(int argc, VALUE *argv, VALUE obj)
*
* Returns the Fiber scheduler, that was last set for the current thread with Fiber.set_scheduler.
* Returns +nil+ if no scheduler is set (which is the default), and non-blocking fibers'
- # behavior is the same as blocking.
+ * behavior is the same as blocking.
* (see "Non-blocking fibers" section in class docs for details about the scheduler concept).
*
*/
@@ -2085,7 +2452,7 @@ rb_fiber_current_scheduler(VALUE klass)
* thread will call scheduler's +close+ method on finalization (allowing the scheduler to
* properly manage all non-finished fibers).
*
- * +scheduler+ can be an object of any class corresponding to Fiber::SchedulerInterface. Its
+ * +scheduler+ can be an object of any class corresponding to Fiber::Scheduler. Its
* implementation is up to the user.
*
* See also the "Non-blocking fibers" section in class docs.
@@ -2152,25 +2519,7 @@ rb_fiber_start(rb_fiber_t *fiber)
rb_fiber_terminate(fiber, need_interrupt, err);
}
-static rb_fiber_t *
-root_fiber_alloc(rb_thread_t *th)
-{
- VALUE fiber_value = fiber_alloc(rb_cFiber);
- rb_fiber_t *fiber = th->ec->fiber_ptr;
-
- VM_ASSERT(DATA_PTR(fiber_value) == NULL);
- VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
- VM_ASSERT(fiber->status == FIBER_RESUMED);
-
- th->root_fiber = fiber;
- DATA_PTR(fiber_value) = fiber;
- fiber->cont.self = fiber_value;
-
- coroutine_initialize_main(&fiber->context);
-
- return fiber;
-}
-
+// Set up a "root fiber", which is the fiber that every Ractor has.
void
rb_threadptr_root_fiber_setup(rb_thread_t *th)
{
@@ -2185,9 +2534,11 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th)
fiber->blocking = 1;
fiber_status_set(fiber, FIBER_RESUMED); /* skip CREATED */
th->ec = &fiber->cont.saved_ec;
- // This skips mjit_cont_new for the initial thread because mjit_enabled is always false
- // at this point. mjit_init calls rb_fiber_init_mjit_cont again for this root_fiber.
- rb_fiber_init_mjit_cont(fiber);
+ // When rb_threadptr_root_fiber_setup is called for the first time, mjit_enabled and
+ // rb_yjit_enabled_p() are still false. So this does nothing and rb_jit_cont_init() that is
+ // called later will take care of it. However, you still have to call cont_init_jit_cont()
+ // here for other Ractors, which are not initialized by rb_jit_cont_init().
+ cont_init_jit_cont(&fiber->cont);
}
void
@@ -2222,16 +2573,6 @@ rb_threadptr_root_fiber_terminate(rb_thread_t *th)
}
static inline rb_fiber_t*
-fiber_current(void)
-{
- rb_execution_context_t *ec = GET_EC();
- if (ec->fiber_ptr->cont.self == 0) {
- root_fiber_alloc(rb_ec_thread_ptr(ec));
- }
- return ec->fiber_ptr;
-}
-
-static inline rb_fiber_t*
return_fiber(bool terminate)
{
rb_fiber_t *fiber = fiber_current();
@@ -2408,7 +2749,60 @@ rb_fiber_transfer(VALUE fiber_value, int argc, const VALUE *argv)
VALUE
rb_fiber_blocking_p(VALUE fiber)
{
- return RBOOL(fiber_ptr(fiber)->blocking != 0);
+ return RBOOL(fiber_ptr(fiber)->blocking);
+}
+
+static VALUE
+fiber_blocking_yield(VALUE fiber_value)
+{
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+ rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr;
+
+ // fiber->blocking is `unsigned int : 1`, so we use it as a boolean:
+ fiber->blocking = 1;
+
+ // Once the fiber is blocking, and current, we increment the thread blocking state:
+ th->blocking += 1;
+
+ return rb_yield(fiber_value);
+}
+
+static VALUE
+fiber_blocking_ensure(VALUE fiber_value)
+{
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+ rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr;
+
+ // We are no longer blocking:
+ fiber->blocking = 0;
+ th->blocking -= 1;
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Fiber.blocking{|fiber| ...} -> result
+ *
+ * Forces the fiber to be blocking for the duration of the block. Returns the
+ * result of the block.
+ *
+ * See the "Non-blocking fibers" section in class docs for details.
+ *
+ */
+VALUE
+rb_fiber_blocking(VALUE class)
+{
+ VALUE fiber_value = rb_fiber_current();
+ rb_fiber_t *fiber = fiber_ptr(fiber_value);
+
+ // If we are already blocking, this is essentially a no-op:
+ if (fiber->blocking) {
+ return rb_yield(fiber_value);
+ }
+ else {
+ return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value);
+ }
}
/*
@@ -2946,329 +3340,6 @@ rb_fiber_pool_initialize(int argc, VALUE* argv, VALUE self)
* fiber.resume #=> FiberError: dead fiber called
*/
-/*
- * Document-class: Fiber::SchedulerInterface
- *
- * This is not an existing class, but documentation of the interface that Scheduler
- * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking
- * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations
- * of some concepts.
- *
- * Scheduler's behavior and usage are expected to be as follows:
- *
- * * When the execution in the non-blocking Fiber reaches some blocking operation (like
- * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's
- * hook methods, listed below.
- * * Scheduler somehow registers what the current fiber is waiting on, and yields control
- * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its
- * wait to end, and other fibers in the same thread can perform)
- * * At the end of the current thread execution, the scheduler's method #close is called
- * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has
- * registered on hook calls) and resuming them when the awaited resource is ready
- * (e.g. I/O ready or sleep time elapsed).
- *
- * A typical implementation would probably rely for this closing loop on a gem like
- * EventMachine[https://github.com/eventmachine/eventmachine] or
- * Async[https://github.com/socketry/async].
- *
- * This way concurrent execution will be achieved transparently for every
- * individual Fiber's code.
- *
- * Hook methods are:
- *
- * * #io_wait, #io_read, and #io_write
- * * #process_wait
- * * #kernel_sleep
- * * #timeout_after
- * * #address_resolve
- * * #block and #unblock
- * * (the list is expanded as Ruby developers make more methods having non-blocking calls)
- *
- * When not specified otherwise, the hook implementations are mandatory: if they are not
- * implemented, the methods trying to call hook will fail. To provide backward compatibility,
- * in the future hooks will be optional (if they are not implemented, due to the scheduler
- * being created for the older Ruby version, the code which needs this hook will not fail,
- * and will just behave in a blocking fashion).
- *
- * It is also strongly recommended that the scheduler implements the #fiber method, which is
- * delegated to by Fiber.schedule.
- *
- * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in
- * <tt>test/fiber/scheduler.rb</tt>
- *
- */
-
-#if 0 /* for RDoc */
-/*
- *
- * Document-method: Fiber::SchedulerInterface#close
- *
- * Called when the current thread exits. The scheduler is expected to implement this
- * method in order to allow all waiting fibers to finalize their execution.
- *
- * The suggested pattern is to implement the main event loop in the #close method.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_close(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#process_wait
- * call-seq: process_wait(pid, flags)
- *
- * Invoked by Process::Status.wait in order to wait for a specified process.
- * See that method description for arguments description.
- *
- * Suggested minimal implementation:
- *
- * Thread.new do
- * Process::Status.wait(pid, flags)
- * end.value
- *
- * This hook is optional: if it is not present in the current scheduler,
- * Process::Status.wait will behave as a blocking method.
- *
- * Expected to return a Process::Status instance.
- */
-static VALUE
-rb_fiber_scheduler_interface_process_wait(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_wait
- * call-seq: io_wait(io, events, timeout)
- *
- * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the
- * specified descriptor is ready for specified events within
- * the specified +timeout+.
- *
- * +events+ is a bit mask of <tt>IO::READABLE</tt>, <tt>IO::WRITABLE</tt>, and
- * <tt>IO::PRIORITY</tt>.
- *
- * Suggested implementation should register which Fiber is waiting for which
- * resources and immediately calling Fiber.yield to pass control to other
- * fibers. Then, in the #close method, the scheduler might dispatch all the
- * I/O resources to fibers waiting for it.
- *
- * Expected to return the subset of events that are ready immediately.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_io_wait(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_read
- * call-seq: io_read(io, buffer, length) -> read length or -errno
- *
- * Invoked by IO#read to read +length+ bytes from +io+ into a specified
- * +buffer+ (see IO::Buffer).
- *
- * The +length+ argument is the "minimum length to be read".
- * If the IO buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to
- * 8KiB might be read, but at least 1KiB will be.
- * Generally, the only case where less data than +length+ will be read is if
- * there is an error reading the data.
- *
- * Specifying a +length+ of 0 is valid and means try reading at least once
- * and return any available data.
- *
- * Suggested implementation should try to read from +io+ in a non-blocking
- * manner and call #io_wait if the +io+ is not ready (which will yield control
- * to other fibers).
- *
- * See IO::Buffer for an interface available to return data.
- *
- * Expected to return number of bytes read, or, in case of an error, <tt>-errno</tt>
- * (negated number corresponding to system's error code).
- *
- * The method should be considered _experimental_.
- */
-static VALUE
-rb_fiber_scheduler_interface_io_read(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#io_write
- * call-seq: io_write(io, buffer, length) -> written length or -errno
- *
- * Invoked by IO#write to write +length+ bytes to +io+ from
- * from a specified +buffer+ (see IO::Buffer).
- *
- * The +length+ argument is the "(minimum) length to be written".
- * If the IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB),
- * at most 8KiB will be written, but at least 1KiB will be.
- * Generally, the only case where less data than +length+ will be written is if
- * there is an error writing the data.
- *
- * Specifying a +length+ of 0 is valid and means try writing at least once,
- * as much data as possible.
- *
- * Suggested implementation should try to write to +io+ in a non-blocking
- * manner and call #io_wait if the +io+ is not ready (which will yield control
- * to other fibers).
- *
- * See IO::Buffer for an interface available to get data from buffer efficiently.
- *
- * Expected to return number of bytes written, or, in case of an error, <tt>-errno</tt>
- * (negated number corresponding to system's error code).
- *
- * The method should be considered _experimental_.
- */
-static VALUE
-rb_fiber_scheduler_interface_io_write(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#kernel_sleep
- * call-seq: kernel_sleep(duration = nil)
- *
- * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide
- * an implementation of sleeping in a non-blocking way. Implementation might
- * register the current fiber in some list of "which fiber wait until what
- * moment", call Fiber.yield to pass control, and then in #close resume
- * the fibers whose wait period has elapsed.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_kernel_sleep(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#address_resolve
- * call-seq: address_resolve(hostname) -> array_of_strings or nil
- *
- * Invoked by any method that performs a non-reverse DNS lookup. The most
- * notable method is Addrinfo.getaddrinfo, but there are many other.
- *
- * The method is expected to return an array of strings corresponding to ip
- * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved.
- *
- * Fairly exhaustive list of all possible call-sites:
- *
- * - Addrinfo.getaddrinfo
- * - Addrinfo.tcp
- * - Addrinfo.udp
- * - Addrinfo.ip
- * - Addrinfo.new
- * - Addrinfo.marshal_load
- * - SOCKSSocket.new
- * - TCPServer.new
- * - TCPSocket.new
- * - IPSocket.getaddress
- * - TCPSocket.gethostbyname
- * - UDPSocket#connect
- * - UDPSocket#bind
- * - UDPSocket#send
- * - Socket.getaddrinfo
- * - Socket.gethostbyname
- * - Socket.pack_sockaddr_in
- * - Socket.sockaddr_in
- * - Socket.unpack_sockaddr_in
- */
-static VALUE
-rb_fiber_scheduler_interface_address_resolve(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#timeout_after
- * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block
- *
- * Invoked by Timeout.timeout to execute the given +block+ within the given
- * +duration+. It can also be invoked directly by the scheduler or user code.
- *
- * Attempt to limit the execution time of a given +block+ to the given
- * +duration+ if possible. When a non-blocking operation causes the +block+'s
- * execution time to exceed the specified +duration+, that non-blocking
- * operation should be interrupted by raising the specified +exception_class+
- * constructed with the given +exception_arguments+.
- *
- * General execution timeouts are often considered risky. This implementation
- * will only interrupt non-blocking operations. This is by design because it's
- * expected that non-blocking operations can fail for a variety of
- * unpredictable reasons, so applications should already be robust in handling
- * these conditions and by implication timeouts.
- *
- * However, as a result of this design, if the +block+ does not invoke any
- * non-blocking operations, it will be impossible to interrupt it. If you
- * desire to provide predictable points for timeouts, consider adding
- * +sleep(0)+.
- *
- * If the block is executed successfully, its result will be returned.
- *
- * The exception will typically be raised using Fiber#raise.
- */
-static VALUE
-rb_fiber_scheduler_interface_timeout_after(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#block
- * call-seq: block(blocker, timeout = nil)
- *
- * Invoked by methods like Thread.join, and by Mutex, to signify that current
- * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has
- * elapsed.
- *
- * +blocker+ is what we are waiting on, informational only (for debugging and
- * logging). There are no guarantee about its value.
- *
- * Expected to return boolean, specifying whether the blocking operation was
- * successful or not.
- */
-static VALUE
-rb_fiber_scheduler_interface_block(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#unblock
- * call-seq: unblock(blocker, fiber)
- *
- * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock
- * calls #block and Mutex#unlock calls #unblock). The scheduler should use
- * the +fiber+ parameter to understand which fiber is unblocked.
- *
- * +blocker+ is what was awaited for, but it is informational only (for debugging
- * and logging), and it is not guaranteed to be the same value as the +blocker+ for
- * #block.
- *
- */
-static VALUE
-rb_fiber_scheduler_interface_unblock(VALUE self)
-{
-}
-
-/*
- * Document-method: SchedulerInterface#fiber
- * call-seq: fiber(&block)
- *
- * Implementation of the Fiber.schedule. The method is <em>expected</em> to immediately
- * run the given block of code in a separate non-blocking fiber, and to return that Fiber.
- *
- * Minimal suggested implementation is:
- *
- * def fiber(&block)
- * fiber = Fiber.new(blocking: false, &block)
- * fiber.resume
- * fiber
- * end
- */
-static VALUE
-rb_fiber_scheduler_interface_fiber(VALUE self)
-{
-}
-#endif
-
void
Init_Cont(void)
{
@@ -3290,6 +3361,7 @@ Init_Cont(void)
fiber_initialize_keywords[0] = rb_intern_const("blocking");
fiber_initialize_keywords[1] = rb_intern_const("pool");
+ fiber_initialize_keywords[2] = rb_intern_const("storage");
const char *fiber_shared_fiber_pool_free_stacks = getenv("RUBY_SHARED_FIBER_POOL_FREE_STACKS");
if (fiber_shared_fiber_pool_free_stacks) {
@@ -3301,8 +3373,14 @@ Init_Cont(void)
rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);
rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
+ rb_define_singleton_method(rb_cFiber, "blocking", rb_fiber_blocking, 0);
+ rb_define_singleton_method(rb_cFiber, "[]", rb_fiber_storage_aref, 1);
+ rb_define_singleton_method(rb_cFiber, "[]=", rb_fiber_storage_aset, 2);
+
rb_define_method(rb_cFiber, "initialize", rb_fiber_initialize, -1);
rb_define_method(rb_cFiber, "blocking?", rb_fiber_blocking_p, 0);
+ rb_define_method(rb_cFiber, "storage", rb_fiber_storage_get, 0);
+ rb_define_method(rb_cFiber, "storage=", rb_fiber_storage_set, 1);
rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1);
rb_define_method(rb_cFiber, "raise", rb_fiber_m_raise, -1);
rb_define_method(rb_cFiber, "backtrace", rb_fiber_backtrace, -1);
@@ -3319,21 +3397,6 @@ Init_Cont(void)
rb_define_singleton_method(rb_cFiber, "schedule", rb_fiber_s_schedule, -1);
-#if 0 /* for RDoc */
- rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "SchedulerInterface", rb_cObject);
- rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_interface_close, 0);
- rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_interface_process_wait, 0);
- rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_interface_io_wait, 0);
- rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_interface_io_read, 0);
- rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_interface_io_write, 0);
- rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_interface_kernel_sleep, 0);
- rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_interface_address_resolve, 0);
- rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_interface_timeout_after, 0);
- rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_interface_block, 0);
- rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_interface_unblock, 0);
- rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler_interface_fiber, 0);
-#endif
-
#ifdef RB_EXPERIMENTAL_FIBER_POOL
rb_cFiberPool = rb_define_class_under(rb_cFiber, "Pool", rb_cObject);
rb_define_alloc_func(rb_cFiberPool, fiber_pool_alloc);
diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h
index 7dba829a1d..71791a4004 100644
--- a/coroutine/asyncify/Context.h
+++ b/coroutine/asyncify/Context.h
@@ -13,6 +13,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <stdint.h>
#include "wasm/asyncify.h"
#include "wasm/machine.h"
#include "wasm/fiber.h"
@@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context)
static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
{
- if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size);
+ // Linear stack pointer must be always aligned down to 16 bytes.
+ // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack
+ uintptr_t sp = ((uintptr_t)stack + size) & ~0xF;
+ if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp);
rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
// record the initial stack pointer position to restore it after resumption
- context->current_sp = (char *)stack + size;
+ context->current_sp = (char *)sp;
context->stack_base = stack;
context->size = size;
}
diff --git a/coroutine/ppc/Context.S b/coroutine/ppc/Context.S
index fe28390df0..cdda93e179 100644
--- a/coroutine/ppc/Context.S
+++ b/coroutine/ppc/Context.S
@@ -1,73 +1,90 @@
+; Based on the code by Samuel Williams. Created by Sergey Fedorov on 04/06/2022.
+; Credits to Samuel Williams, Rei Odaira and Iain Sandoe. Errors, if any, are mine.
+; Some relevant examples: https://github.com/gcc-mirror/gcc/blob/master/libphobos/libdruntime/config/powerpc/switchcontext.S
+; https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/rs6000/darwin-gpsave.S
+; https://www.ibm.com/docs/en/aix/7.2?topic=epilogs-saving-gprs-only
+; ppc32 version may be re-written compactly with stmw/lwm, but the code wonʼt be faster, see: https://github.com/ruby/ruby/pull/5927#issuecomment-1139730541
+
+; Notice that this code is only for Darwin (macOS). Darwin ABI differs from AIX and ELF.
+; To add support for AIX, *BSD or *Linux, please make separate implementations.
+
#define TOKEN_PASTE(x,y) x##y
#define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name)
+.machine ppc7400 ; = G4, Rosetta
.text
-.align 2
.globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
+.align 2
+
PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
- # Make space on the stack for caller registers
- addi r1,r1,-80
+ ; Make space on the stack for caller registers
+ ; (Should we rather use red zone? See libphobos example.)
+ subi r1,r1,80
+
+ ; Get LR
+ mflr r0
- # Save caller registers
- stw r13,0(r1)
- stw r14,4(r1)
- stw r15,8(r1)
- stw r16,12(r1)
- stw r17,16(r1)
- stw r18,20(r1)
- stw r19,24(r1)
- stw r20,28(r1)
- stw r21,32(r1)
+ ; Save caller registers
+ stw r31,0(r1)
+ stw r30,4(r1)
+ stw r29,8(r1)
+ stw r28,12(r1)
+ stw r27,16(r1)
+ stw r26,20(r1)
+ stw r25,24(r1)
+ stw r24,28(r1)
+ stw r23,32(r1)
stw r22,36(r1)
- stw r23,40(r1)
- stw r24,44(r1)
- stw r25,48(r1)
- stw r26,52(r1)
- stw r27,56(r1)
- stw r28,60(r1)
- stw r29,64(r1)
- stw r30,68(r1)
- stw r31,72(r1)
+ stw r21,40(r1)
+ stw r20,44(r1)
+ stw r19,48(r1)
+ stw r18,52(r1)
+ stw r17,56(r1)
+ stw r16,60(r1)
+ stw r15,64(r1)
+ stw r14,68(r1)
+ stw r13,72(r1)
- # Save return address
- mflr r0
+ ; Save return address
+ ; Possibly should rather be saved into linkage area, see libphobos and IBM docs
stw r0,76(r1)
- # Save stack pointer to first argument
+ ; Save stack pointer to first argument
stw r1,0(r3)
- # Load stack pointer from second argument
+ ; Load stack pointer from second argument
lwz r1,0(r4)
- # Restore caller registers
- lwz r13,0(r1)
- lwz r14,4(r1)
- lwz r15,8(r1)
- lwz r16,12(r1)
- lwz r17,16(r1)
- lwz r18,20(r1)
- lwz r19,24(r1)
- lwz r20,28(r1)
- lwz r21,32(r1)
+ ; Load return address
+ lwz r0,76(r1)
+
+ ; Restore caller registers
+ lwz r13,72(r1)
+ lwz r14,68(r1)
+ lwz r15,64(r1)
+ lwz r16,60(r1)
+ lwz r17,56(r1)
+ lwz r18,52(r1)
+ lwz r19,48(r1)
+ lwz r20,44(r1)
+ lwz r21,40(r1)
lwz r22,36(r1)
- lwz r23,40(r1)
- lwz r24,44(r1)
- lwz r25,48(r1)
- lwz r26,52(r1)
- lwz r27,56(r1)
- lwz r28,60(r1)
- lwz r29,64(r1)
- lwz r30,68(r1)
- lwz r31,72(r1)
+ lwz r23,32(r1)
+ lwz r24,28(r1)
+ lwz r25,24(r1)
+ lwz r26,20(r1)
+ lwz r27,16(r1)
+ lwz r28,12(r1)
+ lwz r29,8(r1)
+ lwz r30,4(r1)
+ lwz r31,0(r1)
- # Load return address
- lwz r0,76(r1)
+ ; Set LR
mtlr r0
- # Pop stack frame
+ ; Pop stack frame
addi r1,r1,80
- # Jump to return address
+ ; Jump to return address
blr
-
diff --git a/coroutine/ppc/Context.h b/coroutine/ppc/Context.h
index 9f69390388..1fce112579 100644
--- a/coroutine/ppc/Context.h
+++ b/coroutine/ppc/Context.h
@@ -9,6 +9,7 @@
#include <string.h>
#define COROUTINE __attribute__((noreturn)) void
+#define COROUTINE_LIMITED_ADDRESS_SPACE
enum {
COROUTINE_REGISTERS =
diff --git a/coroutine/ppc64/Context.S b/coroutine/ppc64/Context.S
index 1bd9268f93..f8561e0e7d 100644
--- a/coroutine/ppc64/Context.S
+++ b/coroutine/ppc64/Context.S
@@ -1,70 +1,89 @@
+; Based on the code by Samuel Williams. Created by Sergey Fedorov on 04/06/2022.
+; Credits to Samuel Williams, Rei Odaira and Iain Sandoe. Errors, if any, are mine.
+; Some relevant examples: https://github.com/gcc-mirror/gcc/blob/master/libphobos/libdruntime/config/powerpc/switchcontext.S
+; https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/rs6000/darwin-gpsave.S
+; https://www.ibm.com/docs/en/aix/7.2?topic=epilogs-saving-gprs-only
+
+; Notice that this code is only for Darwin (macOS). Darwin ABI differs from AIX and ELF.
+; To add support for AIX, *BSD or *Linux, please make separate implementations.
+
#define TOKEN_PASTE(x,y) x##y
#define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name)
+.machine ppc64 ; = G5
.text
-.align 3
.globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
-PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
- # Make space on the stack for caller registers
- addi r1,r1,-152
+.align 2
- # Save caller registers
- std r14,0(r1)
- std r15,8(r1)
- std r16,16(r1)
- std r17,24(r1)
- std r18,32(r1)
- std r19,40(r1)
- std r20,48(r1)
- std r21,56(r1)
- std r22,64(r1)
- std r23,72(r1)
- std r24,80(r1)
- std r25,88(r1)
- std r26,96(r1)
- std r27,104(r1)
- std r28,112(r1)
- std r29,120(r1)
- std r30,128(r1)
- std r31,136(r1)
+PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
+ ; Make space on the stack for caller registers
+ ; (Should we rather use red zone? See libphobos example.)
+ subi r1,r1,160
- # Save return address
+ ; Get LR
mflr r0
- std r0,144(r1)
- # Save stack pointer to first argument
+ ; Save caller registers
+ std r31,0(r1)
+ std r30,8(r1)
+ std r29,16(r1)
+ std r28,24(r1)
+ std r27,32(r1)
+ std r26,40(r1)
+ std r25,48(r1)
+ std r24,56(r1)
+ std r23,64(r1)
+ std r22,72(r1)
+ std r21,80(r1)
+ std r20,88(r1)
+ std r19,96(r1)
+ std r18,104(r1)
+ std r17,112(r1)
+ std r16,120(r1)
+ std r15,128(r1)
+ std r14,136(r1)
+ std r13,144(r1)
+
+ ; Save return address
+ ; Possibly should rather be saved into linkage area, see libphobos and IBM docs
+ std r0,152(r1)
+
+ ; Save stack pointer to first argument
std r1,0(r3)
- # Load stack pointer from second argument
+ ; Load stack pointer from second argument
ld r1,0(r4)
- # Restore caller registers
- ld r14,0(r1)
- ld r15,8(r1)
- ld r16,16(r1)
- ld r17,24(r1)
- ld r18,32(r1)
- ld r19,40(r1)
- ld r20,48(r1)
- ld r21,56(r1)
- ld r22,64(r1)
- ld r23,72(r1)
- ld r24,80(r1)
- ld r25,88(r1)
- ld r26,96(r1)
- ld r27,104(r1)
- ld r28,112(r1)
- ld r29,120(r1)
- ld r30,128(r1)
- ld r31,136(r1)
+ ; Load return address
+ ld r0,152(r1)
+
+ ; Restore caller registers
+ ld r13,144(r1)
+ ld r14,136(r1)
+ ld r15,128(r1)
+ ld r16,120(r1)
+ ld r17,112(r1)
+ ld r18,104(r1)
+ ld r19,96(r1)
+ ld r20,88(r1)
+ ld r21,80(r1)
+ ld r22,72(r1)
+ ld r23,64(r1)
+ ld r24,56(r1)
+ ld r25,48(r1)
+ ld r26,40(r1)
+ ld r27,32(r1)
+ ld r28,24(r1)
+ ld r29,16(r1)
+ ld r30,8(r1)
+ ld r31,0(r1)
- # Load return address
- ld r0,144(r1)
+ ; Set LR
mtlr r0
- # Pop stack frame
- addi r1,r1,152
+ ; Pop stack frame
+ addi r1,r1,160
- # Jump to return address
+ ; Jump to return address
blr
diff --git a/coroutine/ppc64/Context.h b/coroutine/ppc64/Context.h
index 5b47511b9c..3e6f77f55a 100644
--- a/coroutine/ppc64/Context.h
+++ b/coroutine/ppc64/Context.h
@@ -12,7 +12,7 @@
enum {
COROUTINE_REGISTERS =
- 19 /* 18 general purpose registers (r14–r31) and 1 return address */
+ 20 /* 19 general purpose registers (r13–r31) and 1 return address */
+ 4 /* space for fiber_entry() to store the link register */
};
@@ -44,7 +44,7 @@ static inline void coroutine_initialize(
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
/* Skip a global prologue that sets the TOC register */
- context->stack_pointer[18] = ((char*)start) + 8;
+ context->stack_pointer[19] = ((char*)start) + 8;
}
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index da00b8aa3c..f342d2fcf7 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -2,15 +2,17 @@ gnumake = yes
include Makefile
-ENABLE_SHARED=@ENABLE_SHARED@
-DLLWRAP = @DLLWRAP@ --target=@target_os@ --driver-name="$(CC)"
+MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode)
+override EXE_LDFLAGS += $(MUNICODE_FLAG)
+
+DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)"
windres-cpp := $(CPP) -xc
windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \
$(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp)))
WINDRES = @WINDRES@ $(windres-cpp) -DRC_INVOKED
STRIP = @STRIP@
-ifeq (@target_os@,cygwin)
+ifeq ($(target_os),cygwin)
DLL_BASE_NAME := $(LIBRUBY_SO:.dll=)
else
DLL_BASE_NAME := $(RUBY_SO_NAME)
@@ -38,7 +40,7 @@ WPROGRAM = $(RUBYW_INSTALL_NAME)$(EXEEXT)
include $(srcdir)/template/GNUmakefile.in
-SOLIBS := $(DLL_BASE_NAME).res.@OBJEXT@ $(SOLIBS)
+SOLIBS := $(DLL_BASE_NAME).res.$(OBJEXT) $(SOLIBS)
override EXTOBJS += $(if $(filter-out $(RUBYW_INSTALL_NAME),$(@:$(EXEEXT)=)),$(RUBY_INSTALL_NAME),$(@:$(EXEEXT)=)).res.$(OBJEXT)
RCFILES = $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(DLL_BASE_NAME).rc
RUBYDEF = $(DLL_BASE_NAME).def
@@ -47,26 +49,26 @@ ruby: $(PROGRAM)
rubyw: $(WPROGRAM)
$(LIBRUBY): $(RUBY_EXP) $(LIBRUBY_SO)
-$(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.@OBJEXT@
+$(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.$(OBJEXT)
-%.res.@OBJEXT@: %.rc
+%.res.$(OBJEXT): %.rc
$(ECHO) compiling $@
$(Q) $(WINDRES) --include-dir . --include-dir $(<D) --include-dir $(srcdir)/win32 $< $@
-%.rc: $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb
+%.rc: $(BOOTSTRAPRUBY_FAKE) $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/win32/resource.rb \
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/win32/resource.rb \
-ruby_name=$(RUBY_INSTALL_NAME) -rubyw_name=$(RUBYW_INSTALL_NAME) \
-so_name=$(DLL_BASE_NAME) -output=$(*F) \
. $(icondirs) $(srcdir)/win32
-$(PROGRAM): $(RUBY_INSTALL_NAME).res.@OBJEXT@
-$(WPROGRAM): $(RUBYW_INSTALL_NAME).res.@OBJEXT@
+$(PROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
+$(WPROGRAM): $(RUBYW_INSTALL_NAME).res.$(OBJEXT)
@rm -f $@
$(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
+ $(Q) $(PURIFY) $(CC) $(MUNICODE_FLAG) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
$(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@
-$(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.@OBJEXT@
+$(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
$(RUBY_EXP): $(LIBRUBY_A)
$(ECHO) creating $@
@@ -78,7 +80,7 @@ $(RUBY_EXP): $(LIBRUBY_A)
GNUmakefile: $(srcdir)/cygwin/GNUmakefile.in
-ifeq (@target_os@,mingw32)
+ifeq ($(target_os),mingw32)
$(OBJS) $(MAINOBJ): win32.h
dir.$(OBJEXT) win32/win32.$(OBJEXT): win32/dir.h
@@ -95,11 +97,11 @@ endif
$(LIBRUBY_SO): $(RUBYDEF)
-$(RUBYDEF): $(LIBRUBY_A) $(PREP) $(RBCONFIG)
+$(RUBYDEF): $(LIBRUBY_A) $(PREP) $(BOOTSTRAPRUBY_FAKE) $(RBCONFIG)
$(ECHO) generating $@
- $(Q) $(MINIRUBY) $(srcdir)/win32/mkexports.rb -output=$@ $(LIBRUBY_A)
+ $(Q) $(BOOTSTRAPRUBY_COMMAND) $(srcdir)/win32/mkexports.rb -output=$@ $(LIBRUBY_A)
clean-local::
@$(RM) $(RUBYDEF)
- @$(RM) $(RUBY_EXP) $(RCFILES:.rc=.res.@OBJEXT@)
+ @$(RM) $(RUBY_EXP) $(RCFILES:.rc=.res.$(OBJEXT))
@$(RM) $(RCFILES)
diff --git a/debug.c b/debug.c
index 3af7f26275..3dd0f71906 100644
--- a/debug.c
+++ b/debug.c
@@ -499,6 +499,7 @@ pretty_filename(const char *path)
return path;
}
+#undef ruby_debug_log
void
ruby_debug_log(const char *file, int line, const char *func_name, const char *fmt, ...)
{
diff --git a/debug_counter.c b/debug_counter.c
index 0fd0e20c6d..463bebf849 100644
--- a/debug_counter.c
+++ b/debug_counter.c
@@ -16,7 +16,7 @@
#if USE_DEBUG_COUNTER
-static const char *const debug_counter_names[] = {
+const char *const rb_debug_counter_names[] = {
#define DEBUG_COUNTER_NAME_EMPTY "" /* Suppress -Wstring-concatenation */
DEBUG_COUNTER_NAME_EMPTY
#undef DEBUG_COUNTER_NAME_EMPTY
@@ -26,7 +26,7 @@ static const char *const debug_counter_names[] = {
};
MJIT_SYMBOL_EXPORT_BEGIN
-size_t rb_debug_counter[numberof(debug_counter_names)];
+size_t rb_debug_counter[numberof(rb_debug_counter_names)];
void rb_debug_counter_add_atomic(enum rb_debug_counter_type type, int add);
MJIT_SYMBOL_EXPORT_END
@@ -56,17 +56,7 @@ void
ruby_debug_counter_reset(void)
{
for (int i = 0; i < RB_DEBUG_COUNTER_MAX; i++) {
- switch (i) {
- case RB_DEBUG_COUNTER_mjit_length_unit_queue:
- case RB_DEBUG_COUNTER_mjit_length_active_units:
- case RB_DEBUG_COUNTER_mjit_length_compact_units:
- case RB_DEBUG_COUNTER_mjit_length_stale_units:
- // These counters may be decreased and should not be reset.
- break;
- default:
- rb_debug_counter[i] = 0;
- break;
- }
+ rb_debug_counter[i] = 0;
}
}
@@ -77,7 +67,7 @@ ruby_debug_counter_get(const char **names_ptr, size_t *counters_ptr)
int i;
if (names_ptr != NULL) {
for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
- names_ptr[i] = debug_counter_names[i];
+ names_ptr[i] = rb_debug_counter_names[i];
}
}
if (counters_ptr != NULL) {
@@ -107,7 +97,7 @@ rb_debug_counter_show_results(const char *msg)
fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%d %s\n", getpid(), msg);
for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%-30s\t%'14"PRIuSIZE"\n",
- debug_counter_names[i],
+ rb_debug_counter_names[i],
rb_debug_counter[i]);
}
}
diff --git a/debug_counter.h b/debug_counter.h
index c6f4176e97..6e0b8dee60 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -130,7 +130,6 @@ RB_DEBUG_COUNTER(frame_C2R)
/* instance variable counts
*
* * ivar_get_ic_hit/miss: ivar_get inline cache (ic) hit/miss counts (VM insn)
- * * ivar_get_ic_miss_serial: ivar_get ic miss reason by serial (VM insn)
* * ivar_get_ic_miss_unset: ... by unset (VM insn)
* * ivar_get_ic_miss_noobject: ... by "not T_OBJECT" (VM insn)
* * ivar_set_...: same counts with ivar_set (VM insn)
@@ -140,17 +139,17 @@ RB_DEBUG_COUNTER(frame_C2R)
*/
RB_DEBUG_COUNTER(ivar_get_ic_hit)
RB_DEBUG_COUNTER(ivar_get_ic_miss)
-RB_DEBUG_COUNTER(ivar_get_ic_miss_serial)
-RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
RB_DEBUG_COUNTER(ivar_get_ic_miss_noobject)
RB_DEBUG_COUNTER(ivar_set_ic_hit)
RB_DEBUG_COUNTER(ivar_set_ic_miss)
-RB_DEBUG_COUNTER(ivar_set_ic_miss_serial)
-RB_DEBUG_COUNTER(ivar_set_ic_miss_unset)
RB_DEBUG_COUNTER(ivar_set_ic_miss_iv_hit)
RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject)
RB_DEBUG_COUNTER(ivar_get_base)
RB_DEBUG_COUNTER(ivar_set_base)
+RB_DEBUG_COUNTER(ivar_get_ic_miss_set)
+RB_DEBUG_COUNTER(ivar_get_cc_miss_set)
+RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
+RB_DEBUG_COUNTER(ivar_get_cc_miss_unset)
/* local variable counts
*
@@ -244,6 +243,7 @@ RB_DEBUG_COUNTER(obj_wb_unprotect)
RB_DEBUG_COUNTER(obj_obj_embed)
RB_DEBUG_COUNTER(obj_obj_transient)
RB_DEBUG_COUNTER(obj_obj_ptr)
+RB_DEBUG_COUNTER(obj_obj_too_complex)
RB_DEBUG_COUNTER(obj_str_ptr)
RB_DEBUG_COUNTER(obj_str_embed)
@@ -347,41 +347,6 @@ RB_DEBUG_COUNTER(vm_sync_lock_enter_nb)
RB_DEBUG_COUNTER(vm_sync_lock_enter_cr)
RB_DEBUG_COUNTER(vm_sync_barrier)
-/* jit_exec() counts */
-RB_DEBUG_COUNTER(jit_exec)
-RB_DEBUG_COUNTER(mjit_exec_not_added)
-RB_DEBUG_COUNTER(mjit_exec_not_ready)
-RB_DEBUG_COUNTER(mjit_exec_not_compiled)
-RB_DEBUG_COUNTER(mjit_exec_call_func)
-
-/* MJIT enqueue / unload */
-RB_DEBUG_COUNTER(mjit_add_iseq_to_process)
-RB_DEBUG_COUNTER(mjit_unload_units)
-
-/* MJIT <-> VM frame push counts */
-RB_DEBUG_COUNTER(mjit_frame_VM2VM)
-RB_DEBUG_COUNTER(mjit_frame_VM2JT)
-RB_DEBUG_COUNTER(mjit_frame_JT2JT)
-RB_DEBUG_COUNTER(mjit_frame_JT2VM)
-
-/* MJIT cancel counters */
-RB_DEBUG_COUNTER(mjit_cancel)
-RB_DEBUG_COUNTER(mjit_cancel_ivar_inline)
-RB_DEBUG_COUNTER(mjit_cancel_exivar_inline)
-RB_DEBUG_COUNTER(mjit_cancel_send_inline)
-RB_DEBUG_COUNTER(mjit_cancel_opt_insn) /* CALL_SIMPLE_METHOD */
-RB_DEBUG_COUNTER(mjit_cancel_invalidate_all)
-RB_DEBUG_COUNTER(mjit_cancel_leave)
-
-/* rb_mjit_unit_list length */
-RB_DEBUG_COUNTER(mjit_length_unit_queue)
-RB_DEBUG_COUNTER(mjit_length_active_units)
-RB_DEBUG_COUNTER(mjit_length_compact_units)
-RB_DEBUG_COUNTER(mjit_length_stale_units)
-
-/* Other MJIT counters */
-RB_DEBUG_COUNTER(mjit_compile_failures)
-
/* load (not implemented yet) */
/*
RB_DEBUG_COUNTER(load_files)
diff --git a/defs/gmake.mk b/defs/gmake.mk
index af4d27a5a2..54fef6685f 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -19,6 +19,7 @@ INSTRUBY_ENV += SDKROOT=
endif
INSTRUBY_ARGS += --gnumake
+ifeq ($(DOT_WAIT),)
CHECK_TARGETS := great exam love check test check% test% btest%
# expand test targets, and those dependents
TEST_TARGETS := $(filter $(CHECK_TARGETS),$(MAKECMDGOALS))
@@ -26,7 +27,7 @@ TEST_DEPENDS := $(filter-out commit $(TEST_TARGETS),$(MAKECMDGOALS))
TEST_TARGETS := $(patsubst great,exam,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out great $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst exam,check,$(TEST_TARGETS))
-TEST_TARGETS := $(patsubst check,test-spec test-all test-tool test-short,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst check,test-syntax-suggest test-spec test-all test-tool test-short,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-rubyspec,test-spec,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out exam check test-spec $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst love,check,$(TEST_TARGETS))
@@ -39,15 +40,13 @@ TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST
TEST_TARGETS := $(patsubst test-bundled-gems,test-bundled-gems-run,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-run,test-bundled-gems-run $(PREPARE_BUNDLED_GEMS),$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-prepare,test-bundled-gems-prepare $(PRECHECK_BUNDLED_GEMS) test-bundled-gems-fetch,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-syntax-suggest,test-syntax-suggest $(PREPARE_SYNTAX_SUGGEST),$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts)
+endif
in-srcdir := $(if $(filter-out .,$(srcdir)),$(CHDIR) $(srcdir) &&)
-ifneq ($(filter -O0 -Od,$(optflags)),)
-override XCFLAGS := $(filter-out -D_FORTIFY_SOURCE=%,$(XCFLAGS))
-endif
-
ifeq ($(if $(filter all main exts enc trans libencs libenc libtrans \
prog program ruby ruby$(EXEEXT) \
wprogram rubyw rubyw$(EXEEXT) \
@@ -75,6 +74,7 @@ $(foreach arch,$(arch_flags),\
$(eval $(call archcmd,$(patsubst -arch=%,%,$(value arch)),$(patsubst -arch=%,-arch %,$(value arch)))))
endif
+ifeq ($(DOT_WAIT),)
.PHONY: $(addprefix yes-,$(TEST_TARGETS))
ifneq ($(filter-out btest%,$(TEST_TARGETS)),)
@@ -84,7 +84,8 @@ endif
ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \
btest-ruby test-knownbug test-basic \
test-testframework test-tool test-ruby test-all \
- test-spec test-bundler-prepare test-bundler test-bundler-parallel \
+ test-spec test-syntax-suggest-prepare test-syntax-suggest \
+ test-bundler-prepare test-bundler test-bundler-parallel \
test-bundled-gems-precheck test-bundled-gems-fetch \
test-bundled-gems-prepare test-bundled-gems-run \
)
@@ -92,6 +93,7 @@ prev_test := $(if $(filter test-spec,$(ORDERED_TEST_TARGETS)),test-spec-precheck
$(foreach test,$(ORDERED_TEST_TARGETS), \
$(eval yes-$(value test) no-$(value test): $(value prev_test)); \
$(eval prev_test := $(value test)))
+endif
ifneq ($(if $(filter install,$(MAKECMDGOALS)),$(filter uninstall,$(MAKECMDGOALS))),)
install-targets := $(filter install uninstall,$(MAKECMDGOALS))
@@ -137,7 +139,7 @@ config.status: $(wildcard config.cache)
STUBPROGRAM = rubystub$(EXEEXT)
IGNOREDPATTERNS = %~ .% %.orig %.rej \#%\#
SCRIPTBINDIR := $(if $(EXEEXT),,exec/)
-SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/libexec/*)))))
+SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/bin/*)))))
stub: $(STUBPROGRAM)
scriptbin: $(SCRIPTPROGRAMS)
@@ -163,9 +165,8 @@ $(SCRIPTBINDIR)%$(EXEEXT): bin/% $(STUBPROGRAM) \
$(Q) chmod +x $@
$(Q) $(POSTLINK)
-$(TIMESTAMPDIR)/.exec.time:
- $(Q) mkdir exec
- $(Q) exit > $@
+$(SCRIPTBINDIR):
+ $(Q) mkdir $@
.PHONY: commit
commit: $(if $(filter commit,$(MAKECMDGOALS)),$(filter-out commit,$(MAKECMDGOALS))) up
@@ -269,8 +270,6 @@ HELP_EXTRA_TASKS = \
" update-github: merge master branch and push it to Pull Request [PR=1234]" \
""
-extract-gems: $(HAVE_BASERUBY:yes=update-gems)
-
# 1. squeeze spaces
# 2. strip and skip comment/empty lines
# 3. "gem x.y.z URL xxxxxx" -> "gem|x.y.z|xxxxxx|URL"
@@ -287,8 +286,18 @@ bundled-gems := $(shell sed \
bundled-gems-rev := $(filter-out $(subst |,,$(bundled-gems)),$(bundled-gems))
bundled-gems := $(filter-out $(bundled-gems-rev),$(bundled-gems))
+# calls $(1) with name, version, revision, URL
+foreach-bundled-gems-rev = \
+ $(foreach g,$(bundled-gems-rev),$(call foreach-bundled-gems-rev-0,$(1),$(subst |, ,$(value g))))
+foreach-bundled-gems-rev-0 = \
+ $(call $(1),$(word 1,$(2)),$(word 2,$(2)),$(word 3,$(2)),$(word 4,$(2)))
+bundled-gem-gemfile = $(srcdir)/gems/$(1)-$(2).gem
+bundled-gem-srcdir = $(srcdir)/gems/src/$(1)
+bundled-gem-extracted = $(srcdir)/.bundle/gems/$(1)-$(2)
+
update-gems: | $(patsubst %,$(srcdir)/gems/%.gem,$(bundled-gems))
-update-gems: | $(foreach g,$(bundled-gems-rev),$(srcdir)/gems/src/$(word 1,$(subst |, ,$(value g))))
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-gemfile)
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-srcdir)
test-bundler-precheck: | $(srcdir)/.bundle/cache
@@ -308,8 +317,7 @@ $(srcdir)/gems/%.gem:
-e 'FileUtils.rm_rf(old.map{'"|n|"'n.chomp(".gem")})'
extract-gems: | $(patsubst %,$(srcdir)/.bundle/gems/%,$(bundled-gems))
-extract-gems: | $(foreach g,$(bundled-gems-rev), \
- $(srcdir)/.bundle/gems/$(word 1,$(subst |, ,$(value g)))-$(word 2,$(subst |, ,$(value g))))
+extract-gems: | $(call foreach-bundled-gems-rev,bundled-gem-extracted)
$(srcdir)/.bundle/gems/%: $(srcdir)/gems/%.gem | .bundle/gems
$(ECHO) Extracting bundle gem $*...
@@ -334,10 +342,10 @@ $(srcdir)/.bundle/gems/$(1)-$(2): | $(srcdir)/gems/src/$(1) .bundle/gems
endef
define copy-gem-0
-$(call copy-gem,$(word 1,$(1)),$(word 2,$(1)),$(word 3,$(1)),$(word 4,$(1)))
+$(eval $(call copy-gem,$(1),$(2),$(3),$(4)))
endef
-$(foreach g,$(bundled-gems-rev),$(eval $(call copy-gem-0,$(subst |, ,$(value g)))))
+$(call foreach-bundled-gems-rev,copy-gem-0)
$(srcdir)/gems/src:
$(MAKEDIRS) $@
@@ -377,28 +385,22 @@ $(MJIT_MIN_HEADER): $(mjit_min_headers) $(PREP)
endif
-ifeq ($(if $(wildcard $(filter-out .,$(UNICODE_FILES) $(UNICODE_PROPERTY_FILES))),,\
- $(wildcard $(srcdir)/lib/unicode_normalize/tables.rb)),)
-# Needs the dependency when any Unicode data file exists, or
-# normalization tables script doesn't. Otherwise, when the target
-# only exists, use it as-is.
-.PHONY: $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time
-UNICODE_TABLES_TIMESTAMP =
-$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: \
- $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
-endif
+.SECONDARY: update-unicode-files
+.SECONDARY: update-unicode-auxiliary-files
+.SECONDARY: update-unicode-ucd-emoji-files
+.SECONDARY: update-unicode-emoji-files
-ifeq ($(wildcard $(srcdir)/revision.h),)
-REVISION_IN_HEADER := none
-REVISION_LATEST := update
-else
-REVISION_IN_HEADER := $(shell sed -n 's/^\#define RUBY_FULL_REVISION "\(.*\)"/\1/p' $(srcdir)/revision.h 2>/dev/null)
+ifeq ($(HAVE_GIT),yes)
REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && $(GIT) log -1 --format=%H 2>/dev/null)
+else
+REVISION_LATEST := update
+endif
+REVISION_IN_HEADER := $(shell sed -n 's/^\#define RUBY_FULL_REVISION "\(.*\)"/\1/p' $(wildcard $(srcdir)/revision.h revision.h) /dev/null 2>/dev/null)
+ifeq ($(REVISION_IN_HEADER),)
+REVISION_IN_HEADER := none
endif
ifneq ($(REVISION_IN_HEADER),$(REVISION_LATEST))
-# GNU make treat the target as unmodified when its dependents get
-# updated but it is not updated, while others may not.
-$(srcdir)/revision.h: $(REVISION_H)
+$(REVISION_H): PHONY
endif
include $(top_srcdir)/yjit/yjit.mk
diff --git a/defs/id.def b/defs/id.def
index 097e34e405..ebf00506ea 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -58,6 +58,7 @@ firstline, predefined = __LINE__+1, %[\
quo
name
nil
+ path
_ UScore
@@ -75,6 +76,7 @@ firstline, predefined = __LINE__+1, %[\
"/*NULL*/" NULL
empty?
eql?
+ default
respond_to? Respond_to
respond_to_missing? Respond_to_missing
<IFUNC>
@@ -194,13 +196,14 @@ predefined.split(/^/).each_with_index do |line, num|
end << token
predefined_ids[token] = name
end
+index = 127
token_ops.split(/^/).each do |line|
next if /^#/ =~ line
line.sub!(/\s+#.*/, '')
id, op, token = line.split
next unless id and op
token ||= (id unless /\A\W\z/ =~ op)
- token_op_ids << [id, op, token]
+ token_op_ids << [id, op, token, (index += 1 if token)]
end
{
"LOCAL" => local_ids,
@@ -212,4 +215,5 @@ end
:preserved => preserved_ids,
:predefined => predefined_ids,
:token_op => token_op_ids,
+ :last_token => index,
}
diff --git a/defs/keywords b/defs/keywords
index fc30ec2d15..a1b1f4f60f 100644
--- a/defs/keywords
+++ b/defs/keywords
@@ -2,7 +2,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/defs/lex.c.src b/defs/lex.c.src
index fc30ec2d15..a1b1f4f60f 100644
--- a/defs/lex.c.src
+++ b/defs/lex.c.src
@@ -2,7 +2,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/dir.c b/dir.c
index a3ea5eea50..3f73f83fc5 100644
--- a/dir.c
+++ b/dir.c
@@ -2305,7 +2305,7 @@ glob_helper(
#endif
break;
case BRACE:
- if (!recursive) {
+ if (!recursive || strchr(p->str, '/')) {
brace = 1;
}
break;
@@ -2932,7 +2932,7 @@ dir_globs(VALUE args, VALUE base, int flags)
static VALUE
dir_glob_option_base(VALUE base)
{
- if (base == Qundef || NIL_P(base)) {
+ if (NIL_OR_UNDEF_P(base)) {
return Qnil;
}
#if USE_OPENDIR_AT
@@ -3343,7 +3343,7 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
RUBY_UBF_IO, 0);
- if (result == Qundef) {
+ if (UNDEF_P(result)) {
rb_sys_fail_path(orig);
}
return result;
diff --git a/dln.c b/dln.c
index b14ba63c44..0edd709bbe 100644
--- a/dln.c
+++ b/dln.c
@@ -41,6 +41,10 @@ static void dln_loaderror(const char *format, ...);
# include <strings.h>
#endif
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+#endif
+
#ifndef xmalloc
void *xmalloc();
void *xcalloc();
@@ -58,7 +62,7 @@ void *xrealloc();
#include <sys/stat.h>
#ifndef S_ISDIR
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifdef HAVE_SYS_PARAM_H
@@ -298,15 +302,15 @@ COMPILER_WARNING_POP
/* assume others than old Mac OS X have no problem */
# define dln_disable_dlclose() false
-#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
-/* targeting newer versions only */
-# define dln_disable_dlclose() false
-
#elif !defined(MAC_OS_X_VERSION_10_11) || \
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11)
/* targeting older versions only */
# define dln_disable_dlclose() true
+#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
+/* targeting newer versions only */
+# define dln_disable_dlclose() false
+
#else
/* support both versions, and check at runtime */
# include <sys/sysctl.h>
diff --git a/doc/.document b/doc/.document
index 5ef2d99651..f589dda07c 100644
--- a/doc/.document
+++ b/doc/.document
@@ -6,3 +6,4 @@ NEWS
syntax
optparse
rdoc
+yjit
diff --git a/doc/ChangeLog-2.3.0 b/doc/ChangeLog-2.3.0
index 629fd9c4ec..94996cffd0 100644
--- a/doc/ChangeLog-2.3.0
+++ b/doc/ChangeLog-2.3.0
@@ -170,7 +170,7 @@ Tue Dec 22 14:31:28 2015 Toru Iwase <tietew@tietew.net>
should return unfrozen new string.
[ruby-core:72426] [Bug #11858]
-Tue Dec 22 05:39:58 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 22 05:39:58 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* ext/cgi/escape/escape.c (preserve_original_state): Preserve
original state for tainted and frozen. [Fix GH-1166]
@@ -208,7 +208,7 @@ Mon Dec 21 09:33:17 2015 Karol Bucek <kares@users.noreply.github.com>
* ext/openssl/lib/openssl/ssl.rb (OpenSSL::SSL::SSLSocket): fix
NotImplementedError typo. [Fix GH-1165]
-Sun Dec 20 20:54:51 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Sun Dec 20 20:54:51 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* cgi/escape/escape.c: Optimize CGI.escapeHTML for
ASCII-compatible encodings. [Fix GH-1164]
@@ -476,7 +476,7 @@ Tue Dec 15 17:57:57 2015 Martin Duerst <duerst@it.aoyama.ac.jp>
to the correct one in the IANA registry (IBM037)
and added an alias (ebcdic-cp-us)
-Tue Dec 15 16:19:26 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 15 16:19:26 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* lib/erb.rb: Render erb with array buffer for function call optimization.
[fix GH-1143]
@@ -488,7 +488,7 @@ Tue Dec 15 13:50:05 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* string.c (rb_str_oct): [DOC] mention radix indicators.
[ruby-core:71310] [Bug #11648]
-Tue Dec 15 12:20:30 2015 Takashi Kokubun <takashikkbn@gmail.com>
+Tue Dec 15 12:20:30 2015 Takashi Kokubun <k0kubun@ruby-lang.org>
* lib/erb.rb: Simplify regexp to optimize erb scanner.
[fix GH-1144]
@@ -2670,7 +2670,7 @@ Sat Nov 7 09:51:38 2015 Koichi Sasada <ko1@atdot.net>
* vm_trace.c (rb_threadptr_exec_event_hooks_orig):
maintain trace_running counter on internal events.
- This patch is made by Takashi Kokubun <takashikkbn@gmail.com>.
+ This patch is made by Takashi Kokubun <k0kubun@ruby-lang.org>.
[Bug #11603] https://github.com/ruby/ruby/pull/1059
Sat Nov 7 03:32:27 2015 Koichi Sasada <ko1@atdot.net>
diff --git a/doc/NEWS/NEWS-3.0.0.md b/doc/NEWS/NEWS-3.0.0.md
index 00c26fe585..bdbd47327b 100644
--- a/doc/NEWS/NEWS-3.0.0.md
+++ b/doc/NEWS/NEWS-3.0.0.md
@@ -512,6 +512,18 @@ Outstanding ones only.
* This version is Ractor compatible.
+* URI
+
+ * URI.escape and URI.unescape have been removed.
+ Instead, use the following methods depending on your specific use case.
+
+ * CGI.escape
+ * URI.encode_www_form
+ * URI.encode_www_form_component
+ * CGI.unescape
+ * URI.decode_www_form
+ * URI.decode_www_form_component
+
## Compatibility issues
Excluding feature bug fixes.
diff --git a/doc/command_injection.rdoc b/doc/command_injection.rdoc
index 8f1303bcf7..af09be23f0 100644
--- a/doc/command_injection.rdoc
+++ b/doc/command_injection.rdoc
@@ -8,7 +8,7 @@ They should not be called with unknown or unsanitized commands.
These methods include:
- Kernel.system
-- {`command` (backtick method)}[rdoc-ref:Kernel#`]
+- {\`command` (backtick method)}[rdoc-ref:Kernel#`]
(also called by the expression <tt>%x[command]</tt>).
- IO.popen(command).
- IO.read(command).
diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md
index 52d6042ec3..469c9d8361 100644
--- a/doc/contributing/building_ruby.md
+++ b/doc/contributing/building_ruby.md
@@ -6,7 +6,7 @@
* C compiler
* autoconf - 2.67 or later
- * bison - 2.0 or later
+ * bison - 3.0 or later
* gperf - 3.0.3 or later
* ruby - 2.7 or later
@@ -18,6 +18,7 @@
* libffi
* libyaml
* libexecinfo (FreeBSD)
+ * rustc - 1.58.0 or later (if you wish to build [YJIT](/doc/yjit/yjit.md))
3. Checkout the CRuby source code:
@@ -25,23 +26,54 @@
git clone https://github.com/ruby/ruby.git
```
-4. Generate the configuration files and build. It's generally advisable to use a build directory:
+4. Generate the configure file:
```
./autogen.sh
- mkdir build && cd build # it's good practice to build outside of source dir
- mkdir ~/.rubies # we will install to .rubies/ruby-master in our home dir
+ ```
+
+5. Create a `build` directory outside of the source directory:
+
+ ```
+ mkdir build && cd build
+ ```
+
+ While it's not necessary to build in a separate directory, it's good practice to do so.
+
+6. We'll install Ruby in `~/.rubies/ruby-master`, so create the directory:
+
+ ```
+ mkdir ~/.rubies
+ ```
+
+7. Run configure:
+
+ ```
../configure --prefix="${HOME}/.rubies/ruby-master"
- make install
```
-5. Optional: If you are frequently building Ruby, disabling documentation will reduce the time it takes to `make`:
+ - If you are frequently building Ruby, add the `--disable-install-doc` flag to not build documentation which will speed up the build process.
- ``` shell
- ../configure --disable-install-doc
+8. Build Ruby:
+
+ ```
+ make install
```
-6. [Run tests](testing_ruby.md) to confirm your build succeeded
+ - If you're on macOS and installed \OpenSSL through Homebrew, you may encounter failure to build \OpenSSL that look like this:
+
+ ```
+ openssl:
+ Could not be configured. It will not be installed.
+ ruby/ext/openssl/extconf.rb: OpenSSL library could not be found. You might want to use --with-openssl-dir=<dir> option to specify the prefix where OpenSSL is installed.
+ Check ext/openssl/mkmf.log for more details.
+ ```
+
+ Adding `--with-openssl-dir=$(brew --prefix openssl)` to the list of options passed to configure may solve the issue.
+
+ Remember to delete your `build` directory and start again from the configure step.
+
+9. [Run tests](testing_ruby.md) to confirm your build succeeded.
### Unexplainable Build Errors
diff --git a/doc/contributing/documentation_guide.md b/doc/contributing/documentation_guide.md
index df67747710..9cfd59d629 100644
--- a/doc/contributing/documentation_guide.md
+++ b/doc/contributing/documentation_guide.md
@@ -137,6 +137,19 @@ or [list](rdoc-ref:RDoc::Markup@Simple+Lists)
should be preceded by and followed by a blank line.
This is unnecessary for the HTML output, but helps in the `ri` output.
+### \Method Names
+
+For a method name in text:
+
+- For a method in the current class or module,
+ use a double-colon for a singleton method,
+ or a hash mark for an instance method:
+ <tt>::bar</tt>, <tt>#baz</tt>.
+- Otherwise, include the class or module name
+ and use a dot for a singleton method,
+ or a hash mark for an instance method:
+ <tt>Foo.bar</tt>, <tt>Foo#baz</tt>.
+
### Auto-Linking
In general, \RDoc's auto-linking should not be suppressed.
@@ -151,6 +164,28 @@ We might consider whether to suppress when:
- The same reference is repeated many times
(e.g., _RDoc_ on this page).
+### HTML Tags
+
+In general, avoid using HTML tags (even in formats where it's allowed)
+because `ri` (the Ruby Interactive reference tool)
+may not render them properly.
+
+### Tables
+
+In particular, avoid building tables with HTML tags
+(<tt><table></tt>, etc.).
+
+Alternatives are:
+
+- The GFM (GitHub Flavored Markdown) table extension,
+ which is enabled by default. See
+ {GFM tables extension}[https://github.github.com/gfm/#tables-extension-].
+
+- A {verbatim text block}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks],
+ using spaces and punctuation to format the text.
+ Note that {text markup}[rdoc-ref:RDoc::MarkupReference@Text+Markup]
+ will not be honored.
+
## Documenting Classes and Modules
The general structure of the class or module documentation should be:
diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc
index c61ab11e9a..1f3c54d740 100644
--- a/doc/encodings.rdoc
+++ b/doc/encodings.rdoc
@@ -467,12 +467,13 @@ These keyword-value pairs specify encoding options:
with a carriage-return character (<tt>"\r"</tt>).
- <tt>:crlf_newline: true</tt>: Replace each line-feed character (<tt>"\n"</tt>)
with a carriage-return/line-feed string (<tt>"\r\n"</tt>).
- - <tt>:universal_newline: true</tt>: Replace each carriage-return/line-feed string
+ - <tt>:universal_newline: true</tt>: Replace each carriage-return
+ character (<tt>"\r"</tt>) and each carriage-return/line-feed string
(<tt>"\r\n"</tt>) with a line-feed character (<tt>"\n"</tt>).
Examples:
- s = "\n \r\n" # => "\n \r\n"
- s.encode('ASCII', cr_newline: true) # => "\r \r\r"
- s.encode('ASCII', crlf_newline: true) # => "\r\n \r\r\n"
- s.encode('ASCII', universal_newline: true) # => "\n \n"
+ s = "\n \r \r\n" # => "\n \r \r\n"
+ s.encode('ASCII', cr_newline: true) # => "\r \r \r\r"
+ s.encode('ASCII', crlf_newline: true) # => "\r\n \r \r\r\n"
+ s.encode('ASCII', universal_newline: true) # => "\n \n \n"
diff --git a/doc/examples/files.rdoc b/doc/examples/files.rdoc
new file mode 100644
index 0000000000..f736132770
--- /dev/null
+++ b/doc/examples/files.rdoc
@@ -0,0 +1,26 @@
+# English text with newlines.
+text = <<~EOT
+ First line
+ Second line
+
+ Fourth line
+ Fifth line
+EOT
+
+# Russian text.
+russian = "\u{442 435 441 442}" # => "тест"
+
+# Binary data.
+data = "\u9990\u9991\u9992\u9993\u9994"
+
+# Text file.
+File.write('t.txt', text)
+
+# File with Russian text.
+File.write('t.rus', russian)
+
+# File with binary data.
+f = File.new('t.dat', 'wb:UTF-16')
+f.write(data)
+f.close
+
diff --git a/doc/mjit/mjit.md b/doc/mjit/mjit.md
new file mode 100644
index 0000000000..6f19ab3ea7
--- /dev/null
+++ b/doc/mjit/mjit.md
@@ -0,0 +1,39 @@
+# MJIT
+
+This document has some tips that might be useful when you work on MJIT.
+
+## Supported platforms
+
+The following platforms are either tested on CI or assumed to work.
+
+* OS: Linux, macOS
+* Arch: x86\_64, aarch64, arm64, i686, i386
+
+### Not supported
+
+The MJIT support for the following platforms is no longer maintained.
+
+* OS: Windows (mswin, MinGW), Solaris
+* Arch: SPARC, s390x
+
+## Developing MJIT
+
+### Bindgen
+
+If you see an "MJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job.
+
+For doing the same thing locally, run `make mjit-bindgen` after installing libclang.
+macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`.
+
+### Always run make install
+
+Always run `make install` before running MJIT. It could easily cause a SEGV if you don't.
+MJIT looks for the installed header for security reasons.
+
+### --mjit-debug vs --mjit-debug=-ggdb3
+
+`--mjit-debug=[flags]` allows you to specify arbitrary flags while keeping other compiler flags like `-O3`,
+which is useful for profiling benchmarks.
+
+`--mjit-debug` alone, on the other hand, disables `-O3` and adds debug flags.
+If you're debugging MJIT, what you need to use is not `--mjit-debug=-ggdb3` but `--mjit-debug`.
diff --git a/doc/net-http/examples.rdoc b/doc/net-http/examples.rdoc
new file mode 100644
index 0000000000..c1366e7ad1
--- /dev/null
+++ b/doc/net-http/examples.rdoc
@@ -0,0 +1,31 @@
+Examples here assume that <tt>net/http</tt> has been required
+(which also requires +uri+):
+
+ require 'net/http'
+
+Many code examples here use these example websites:
+
+- https://jsonplaceholder.typicode.com.
+- http://example.com.
+
+Some examples also assume these variables:
+
+ uri = URI('https://jsonplaceholder.typicode.com/')
+ uri.freeze # Examples may not modify.
+ hostname = uri.hostname # => "jsonplaceholder.typicode.com"
+ path = uri.path # => "/"
+ port = uri.port # => 443
+
+So that example requests may be written as:
+
+ Net::HTTP.get(uri)
+ Net::HTTP.get(hostname, '/index.html')
+ Net::HTTP.start(hostname) do |http|
+ http.get('/todos/1')
+ http.get('/todos/2')
+ end
+
+An example that needs a modified URI first duplicates +uri+, then modifies the duplicate:
+
+ _uri = uri.dup
+ _uri.path = '/todos/1'
diff --git a/doc/net-http/included_getters.rdoc b/doc/net-http/included_getters.rdoc
new file mode 100644
index 0000000000..7ac327f4b4
--- /dev/null
+++ b/doc/net-http/included_getters.rdoc
@@ -0,0 +1,3 @@
+This class also includes (indirectly) module Net::HTTPHeader,
+which gives access to its
+{methods for getting headers}[rdoc-ref:Net::HTTPHeader@Getters].
diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc
new file mode 100644
index 0000000000..ec13b24c69
--- /dev/null
+++ b/doc/packed_data.rdoc
@@ -0,0 +1,590 @@
+== Packed \Data
+
+Certain Ruby core methods deal with packing and unpacking data:
+
+- \Method Array#pack:
+ Formats each element in array +self+ into a binary string;
+ returns that string.
+- \Method String#unpack:
+ Extracts data from string +self+,
+ forming objects that become the elements of a new array;
+ returns that array.
+- \Method String#unpack1:
+ Does the same, but unpacks and returns only the first extracted object.
+
+Each of these methods accepts a string +template+,
+consisting of zero or more _directive_ characters,
+each followed by zero or more _modifier_ characters.
+
+Examples (directive <tt>'C'</tt> specifies 'unsigned character'):
+
+ [65].pack('C') # => "A" # One element, one directive.
+ [65, 66].pack('CC') # => "AB" # Two elements, two directives.
+ [65, 66].pack('C') # => "A" # Extra element is ignored.
+ [65].pack('') # => "" # No directives.
+ [65].pack('CC') # Extra directive raises ArgumentError.
+
+ 'A'.unpack('C') # => [65] # One character, one directive.
+ 'AB'.unpack('CC') # => [65, 66] # Two characters, two directives.
+ 'AB'.unpack('C') # => [65] # Extra character is ignored.
+ 'A'.unpack('CC') # => [65, nil] # Extra directive generates nil.
+ 'AB'.unpack('') # => [] # No directives.
+
+The string +template+ may contain any mixture of valid directives
+(directive <tt>'c'</tt> specifies 'signed character'):
+
+ [65, -1].pack('cC') # => "A\xFF"
+ "A\xFF".unpack('cC') # => [65, 255]
+
+The string +template+ may contain whitespace (which is ignored)
+and comments, each of which begins with character <tt>'#'</tt>
+and continues up to and including the next following newline:
+
+ [0,1].pack(" C #foo \n C ") # => "\x00\x01"
+ "\0\1".unpack(" C #foo \n C ") # => [0, 1]
+
+Any directive may be followed by either of these modifiers:
+
+- <tt>'*'</tt> - The directive is to be applied as many times as needed:
+
+ [65, 66].pack('C*') # => "AB"
+ 'AB'.unpack('C*') # => [65, 66]
+
+- Integer +count+ - The directive is to be applied +count+ times:
+
+ [65, 66].pack('C2') # => "AB"
+ [65, 66].pack('C3') # Raises ArgumentError.
+ 'AB'.unpack('C2') # => [65, 66]
+ 'AB'.unpack('C3') # => [65, 66, nil]
+
+ Note: Directives in <tt>%w[A a Z m]</tt> use +count+ differently;
+ see {String Directives}[rdoc-ref:packed_data.rdoc@String+Directives].
+
+If elements don't fit the provided directive, only least significant bits are encoded:
+
+ [257].pack("C").unpack("C") # => [1]
+
+=== Packing \Method
+
+\Method Array#pack accepts optional keyword argument
++buffer+ that specifies the target string (instead of a new string):
+
+ [65, 66].pack('C*', buffer: 'foo') # => "fooAB"
+
+The method can accept a block:
+
+ # Packed string is passed to the block.
+ [65, 66].pack('C*') {|s| p s } # => "AB"
+
+=== Unpacking Methods
+
+Methods String#unpack and String#unpack1 each accept
+an optional keyword argument +offset+ that specifies an offset
+into the string:
+
+ 'ABC'.unpack('C*', offset: 1) # => [66, 67]
+ 'ABC'.unpack1('C*', offset: 1) # => 66
+
+Both methods can accept a block:
+
+ # Each unpacked object is passed to the block.
+ ret = []
+ "ABCD".unpack("C*") {|c| ret << c }
+ ret # => [65, 66, 67, 68]
+
+ # The single unpacked object is passed to the block.
+ 'AB'.unpack1('C*') {|ele| p ele } # => 65
+
+=== \Integer Directives
+
+Each integer directive specifies the packing or unpacking
+for one element in the input or output array.
+
+==== 8-Bit \Integer Directives
+
+- <tt>'c'</tt> - 8-bit signed integer
+ (like C <tt>signed char</tt>):
+
+ [0, 1, 255].pack('c*') # => "\x00\x01\xFF"
+ s = [0, 1, -1].pack('c*') # => "\x00\x01\xFF"
+ s.unpack('c*') # => [0, 1, -1]
+
+- <tt>'C'</tt> - 8-bit signed integer
+ (like C <tt>unsigned char</tt>):
+
+ [0, 1, 255].pack('C*') # => "\x00\x01\xFF"
+ s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF"
+ s.unpack('C*') # => [0, 1, 255]
+
+==== 16-Bit \Integer Directives
+
+- <tt>'s'</tt> - 16-bit signed integer, native-endian
+ (like C <tt>int16_t</tt>):
+
+ [513, -514].pack('s*') # => "\x01\x02\xFE\xFD"
+ s = [513, 65022].pack('s*') # => "\x01\x02\xFE\xFD"
+ s.unpack('s*') # => [513, -514]
+
+- <tt>'S'</tt> - 16-bit unsigned integer, native-endian
+ (like C <tt>uint16_t</tt>):
+
+ [513, -514].pack('S*') # => "\x01\x02\xFE\xFD"
+ s = [513, 65022].pack('S*') # => "\x01\x02\xFE\xFD"
+ s.unpack('S*') # => [513, 65022]
+
+- <tt>'n'</tt> - 16-bit network integer, big-endian:
+
+ s = [0, 1, -1, 32767, -32768, 65535].pack('n*')
+ # => "\x00\x00\x00\x01\xFF\xFF\x7F\xFF\x80\x00\xFF\xFF"
+ s.unpack('n*')
+ # => [0, 1, 65535, 32767, 32768, 65535]
+
+- <tt>'v'</tt> - 16-bit VAX integer, little-endian:
+
+ s = [0, 1, -1, 32767, -32768, 65535].pack('v*')
+ # => "\x00\x00\x01\x00\xFF\xFF\xFF\x7F\x00\x80\xFF\xFF"
+ s.unpack('v*')
+ # => [0, 1, 65535, 32767, 32768, 65535]
+
+==== 32-Bit \Integer Directives
+
+- <tt>'l'</tt> - 32-bit signed integer, native-endian
+ (like C <tt>int32_t</tt>):
+
+ s = [67305985, -50462977].pack('l*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('l*')
+ # => [67305985, -50462977]
+
+- <tt>'L'</tt> - 32-bit unsigned integer, native-endian
+ (like C <tt>uint32_t</tt>):
+
+ s = [67305985, 4244504319].pack('L*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('L*')
+ # => [67305985, 4244504319]
+
+- <tt>'N'</tt> - 32-bit network integer, big-endian:
+
+ s = [0,1,-1].pack('N*')
+ # => "\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF"
+ s.unpack('N*')
+ # => [0, 1, 4294967295]
+
+- <tt>'V'</tt> - 32-bit VAX integer, little-endian:
+
+ s = [0,1,-1].pack('V*')
+ # => "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF"
+ s.unpack('v*')
+ # => [0, 0, 1, 0, 65535, 65535]
+
+==== 64-Bit \Integer Directives
+
+- <tt>'q'</tt> - 64-bit signed integer, native-endian
+ (like C <tt>int64_t</tt>):
+
+ s = [578437695752307201, -506097522914230529].pack('q*')
+ # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
+ s.unpack('q*')
+ # => [578437695752307201, -506097522914230529]
+
+- <tt>'Q'</tt> - 64-bit unsigned integer, native-endian
+ (like C <tt>uint64_t</tt>):
+
+ s = [578437695752307201, 17940646550795321087].pack('Q*')
+ # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
+ s.unpack('Q*')
+ # => [578437695752307201, 17940646550795321087]
+
+==== Platform-Dependent \Integer Directives
+
+- <tt>'i'</tt> - Platform-dependent width signed integer,
+ native-endian (like C <tt>int</tt>):
+
+ s = [67305985, -50462977].pack('i*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('i*')
+ # => [67305985, -50462977]
+
+- <tt>'I'</tt> - Platform-dependent width unsigned integer,
+ native-endian (like C <tt>unsigned int</tt>):
+
+ s = [67305985, -50462977].pack('I*')
+ # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
+ s.unpack('I*')
+ # => [67305985, 4244504319]
+
+==== Pointer Directives
+
+- <tt>'j'</tt> - 64-bit pointer-width signed integer,
+ native-endian (like C <tt>intptr_t</tt>):
+
+ s = [67305985, -50462977].pack('j*')
+ # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF"
+ s.unpack('j*')
+ # => [67305985, -50462977]
+
+- <tt>'j'</tt> - 64-bit pointer-width unsigned integer,
+ native-endian (like C <tt>uintptr_t</tt>):
+
+ s = [67305985, 4244504319].pack('J*')
+ # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00"
+ s.unpack('J*')
+ # => [67305985, 4244504319]
+
+==== Other \Integer Directives
+:
+- <tt>'U'</tt> - UTF-8 character:
+
+ s = [4194304].pack('U*')
+ # => "\xF8\x90\x80\x80\x80"
+ s.unpack('U*')
+ # => [4194304]
+
+- <tt>'w'</tt> - BER-encoded integer
+ (see {BER enocding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]):
+
+ s = [1073741823].pack('w*')
+ # => "\x83\xFF\xFF\xFF\x7F"
+ s.unpack('w*')
+ # => [1073741823]
+
+==== Modifiers for \Integer Directives
+
+For directives in
+<tt>'i'</tt>,
+<tt>'I'</tt>,
+<tt>'s'</tt>,
+<tt>'S'</tt>,
+<tt>'l'</tt>,
+<tt>'L'</tt>,
+<tt>'q'</tt>,
+<tt>'Q'</tt>,
+<tt>'j'</tt>, and
+<tt>'J'</tt>,
+these modifiers may be suffixed:
+
+- <tt>'!'</tt> or <tt>'_'</tt> - Underlying platform’s native size.
+- <tt>'>'</tt> - Big-endian.
+- <tt>'<'</tt> - Little-endian.
+
+=== \Float Directives
+
+Each float directive specifies the packing or unpacking
+for one element in the input or output array.
+
+==== Single-Precision \Float Directives
+
+- <tt>'F'</tt> or <tt>'f'</tt> - Native format:
+
+ s = [3.0].pack('F') # => "\x00\x00@@"
+ s.unpack('F') # => [3.0]
+
+- <tt>'e'</tt> - Little-endian:
+
+ s = [3.0].pack('e') # => "\x00\x00@@"
+ s.unpack('e') # => [3.0]
+
+- <tt>'g'</tt> - Big-endian:
+
+ s = [3.0].pack('g') # => "@@\x00\x00"
+ s.unpack('g') # => [3.0]
+
+==== Double-Precision \Float Directives
+
+- <tt>'D'</tt> or <tt>'d'</tt> - Native format:
+
+ s = [3.0].pack('D') # => "\x00\x00\x00\x00\x00\x00\b@"
+ s.unpack('D') # => [3.0]
+
+- <tt>'E'</tt> - Little-endian:
+
+ s = [3.0].pack('E') # => "\x00\x00\x00\x00\x00\x00\b@"
+ s.unpack('E') # => [3.0]
+
+- <tt>'G'</tt> - Big-endian:
+
+ s = [3.0].pack('G') # => "@\b\x00\x00\x00\x00\x00\x00"
+ s.unpack('G') # => [3.0]
+
+A float directive may be infinity or not-a-number:
+
+ inf = 1.0/0.0 # => Infinity
+ [inf].pack('f') # => "\x00\x00\x80\x7F"
+ "\x00\x00\x80\x7F".unpack('f') # => [Infinity]
+
+ nan = inf/inf # => NaN
+ [nan].pack('f') # => "\x00\x00\xC0\x7F"
+ "\x00\x00\xC0\x7F".unpack('f') # => [NaN]
+
+=== \String Directives
+
+Each string directive specifies the packing or unpacking
+for one byte in the input or output string.
+
+==== Binary \String Directives
+
+- <tt>'A'</tt> - Arbitrary binary string (space padded; count is width);
+ +nil+ is treated as the empty string:
+
+ ['foo'].pack('A') # => "f"
+ ['foo'].pack('A*') # => "foo"
+ ['foo'].pack('A2') # => "fo"
+ ['foo'].pack('A4') # => "foo "
+ [nil].pack('A') # => " "
+ [nil].pack('A*') # => ""
+ [nil].pack('A2') # => " "
+ [nil].pack('A4') # => " "
+
+ "foo\0".unpack('A') # => ["f"]
+ "foo\0".unpack('A4') # => ["foo"]
+ "foo\0bar".unpack('A10') # => ["foo\x00bar"] # Reads past "\0".
+ "foo ".unpack('A') # => ["f"]
+ "foo ".unpack('A4') # => ["foo"]
+ "foo".unpack('A4') # => ["foo"]
+
+ russian = "\u{442 435 441 442}" # => "тест"
+ russian.size # => 4
+ russian.bytesize # => 8
+ [russian].pack('A') # => "\xD1"
+ [russian].pack('A*') # => "\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"
+ russian.unpack('A') # => ["\xD1"]
+ russian.unpack('A2') # => ["\xD1\x82"]
+ russian.unpack('A4') # => ["\xD1\x82\xD0\xB5"]
+ russian.unpack('A*') # => ["\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"]
+
+- <tt>'a'</tt> - Arbitrary binary string (null padded; count is width):
+
+ ["foo"].pack('a') # => "f"
+ ["foo"].pack('a*') # => "foo"
+ ["foo"].pack('a2') # => "fo"
+ ["foo\0"].pack('a4') # => "foo\x00"
+ [nil].pack('a') # => "\x00"
+ [nil].pack('a*') # => ""
+ [nil].pack('a2') # => "\x00\x00"
+ [nil].pack('a4') # => "\x00\x00\x00\x00"
+
+ "foo\0".unpack('a') # => ["f"]
+ "foo\0".unpack('a4') # => ["foo\x00"]
+ "foo ".unpack('a4') # => ["foo "]
+ "foo".unpack('a4') # => ["foo"]
+ "foo\0bar".unpack('a4') # => ["foo\x00"] # Reads past "\0".
+
+- <tt>'Z'</tt> - Same as <tt>'a'</tt>,
+ except that null is added or ignored with <tt>'*'</tt>:
+
+ ["foo"].pack('Z*') # => "foo\x00"
+ [nil].pack('Z*') # => "\x00"
+
+ "foo\0".unpack('Z*') # => ["foo"]
+ "foo".unpack('Z*') # => ["foo"]
+ "foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0".
+
+==== Bit \String Directives
+
+- <tt>'B'</tt> - Bit string (high byte first):
+
+ ['11111111' + '00000000'].pack('B*') # => "\xFF\x00"
+ ['10000000' + '01000000'].pack('B*') # => "\x80@"
+
+ ['1'].pack('B0') # => ""
+ ['1'].pack('B1') # => "\x80"
+ ['1'].pack('B2') # => "\x80\x00"
+ ['1'].pack('B3') # => "\x80\x00"
+ ['1'].pack('B4') # => "\x80\x00\x00"
+ ['1'].pack('B5') # => "\x80\x00\x00"
+ ['1'].pack('B6') # => "\x80\x00\x00\x00"
+
+ "\xff\x00".unpack("B*") # => ["1111111100000000"]
+ "\x01\x02".unpack("B*") # => ["0000000100000010"]
+
+ "".unpack("B0") # => [""]
+ "\x80".unpack("B1") # => ["1"]
+ "\x80".unpack("B2") # => ["10"]
+ "\x80".unpack("B3") # => ["100"]
+
+- <tt>'b'</tt> - Bit string (low byte first):
+
+ ['11111111' + '00000000'].pack('b*') # => "\xFF\x00"
+ ['10000000' + '01000000'].pack('b*') # => "\x01\x02"
+
+ ['1'].pack('b0') # => ""
+ ['1'].pack('b1') # => "\x01"
+ ['1'].pack('b2') # => "\x01\x00"
+ ['1'].pack('b3') # => "\x01\x00"
+ ['1'].pack('b4') # => "\x01\x00\x00"
+ ['1'].pack('b5') # => "\x01\x00\x00"
+ ['1'].pack('b6') # => "\x01\x00\x00\x00"
+
+ "\xff\x00".unpack("b*") # => ["1111111100000000"]
+ "\x01\x02".unpack("b*") # => ["1000000001000000"]
+
+ "".unpack("b0") # => [""]
+ "\x01".unpack("b1") # => ["1"]
+ "\x01".unpack("b2") # => ["10"]
+ "\x01".unpack("b3") # => ["100"]
+
+==== Hex \String Directives
+
+- <tt>'H'</tt> - Hex string (high nibble first):
+
+ ['10ef'].pack('H*') # => "\x10\xEF"
+ ['10ef'].pack('H0') # => ""
+ ['10ef'].pack('H3') # => "\x10\xE0"
+ ['10ef'].pack('H5') # => "\x10\xEF\x00"
+
+ ['fff'].pack('H3') # => "\xFF\xF0"
+ ['fff'].pack('H4') # => "\xFF\xF0"
+ ['fff'].pack('H5') # => "\xFF\xF0\x00"
+ ['fff'].pack('H6') # => "\xFF\xF0\x00"
+ ['fff'].pack('H7') # => "\xFF\xF0\x00\x00"
+ ['fff'].pack('H8') # => "\xFF\xF0\x00\x00"
+
+ "\x10\xef".unpack('H*') # => ["10ef"]
+ "\x10\xef".unpack('H0') # => [""]
+ "\x10\xef".unpack('H1') # => ["1"]
+ "\x10\xef".unpack('H2') # => ["10"]
+ "\x10\xef".unpack('H3') # => ["10e"]
+ "\x10\xef".unpack('H4') # => ["10ef"]
+ "\x10\xef".unpack('H5') # => ["10ef"]
+
+- <tt>'h'</tt> - Hex string (low nibble first):
+
+ ['10ef'].pack('h*') # => "\x01\xFE"
+ ['10ef'].pack('h0') # => ""
+ ['10ef'].pack('h3') # => "\x01\x0E"
+ ['10ef'].pack('h5') # => "\x01\xFE\x00"
+
+ ['fff'].pack('h3') # => "\xFF\x0F"
+ ['fff'].pack('h4') # => "\xFF\x0F"
+ ['fff'].pack('h5') # => "\xFF\x0F\x00"
+ ['fff'].pack('h6') # => "\xFF\x0F\x00"
+ ['fff'].pack('h7') # => "\xFF\x0F\x00\x00"
+ ['fff'].pack('h8') # => "\xFF\x0F\x00\x00"
+
+ "\x01\xfe".unpack('h*') # => ["10ef"]
+ "\x01\xfe".unpack('h0') # => [""]
+ "\x01\xfe".unpack('h1') # => ["1"]
+ "\x01\xfe".unpack('h2') # => ["10"]
+ "\x01\xfe".unpack('h3') # => ["10e"]
+ "\x01\xfe".unpack('h4') # => ["10ef"]
+ "\x01\xfe".unpack('h5') # => ["10ef"]
+
+==== Pointer \String Directives
+
+- <tt>'P'</tt> - Pointer to a structure (fixed-length string):
+
+ s = ['abc'].pack('P') # => "\xE0O\x7F\xE5\xA1\x01\x00\x00"
+ s.unpack('P*') # => ["abc"]
+ ".".unpack("P") # => []
+ ("\0" * 8).unpack("P") # => [nil]
+ [nil].pack("P") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+
+- <tt>'p'</tt> - Pointer to a null-terminated string:
+
+ s = ['abc'].pack('p') # => "(\xE4u\xE5\xA1\x01\x00\x00"
+ s.unpack('p*') # => ["abc"]
+ ".".unpack("p") # => []
+ ("\0" * 8).unpack("p") # => [nil]
+ [nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+
+==== Other \String Directives
+
+- <tt>'M'</tt> - Quoted printable, MIME encoding;
+ text mode, but input must use LF and output LF;
+ (see {RFC 2045}[https://www.ietf.org/rfc/rfc2045.txt]):
+
+ ["a b c\td \ne"].pack('M') # => "a b c\td =\n\ne=\n"
+ ["\0"].pack('M') # => "=00=\n"
+
+ ["a"*1023].pack('M') == ("a"*73+"=\n")*14+"a=\n" # => true
+ ("a"*73+"=\na=\n").unpack('M') == ["a"*74] # => true
+ (("a"*73+"=\n")*14+"a=\n").unpack('M') == ["a"*1023] # => true
+
+ "a b c\td =\n\ne=\n".unpack('M') # => ["a b c\td \ne"]
+ "=00=\n".unpack('M') # => ["\x00"]
+
+ "pre=31=32=33after".unpack('M') # => ["pre123after"]
+ "pre=\nafter".unpack('M') # => ["preafter"]
+ "pre=\r\nafter".unpack('M') # => ["preafter"]
+ "pre=".unpack('M') # => ["pre="]
+ "pre=\r".unpack('M') # => ["pre=\r"]
+ "pre=hoge".unpack('M') # => ["pre=hoge"]
+ "pre==31after".unpack('M') # => ["pre==31after"]
+ "pre===31after".unpack('M') # => ["pre===31after"]
+
+- <tt>'m'</tt> - Base64 encoded string;
+ count specifies input bytes between each newline,
+ rounded down to nearest multiple of 3;
+ if count is zero, no newlines are added;
+ (see {RFC 4648}[https://www.ietf.org/rfc/rfc4648.txt]):
+
+ [""].pack('m') # => ""
+ ["\0"].pack('m') # => "AA==\n"
+ ["\0\0"].pack('m') # => "AAA=\n"
+ ["\0\0\0"].pack('m') # => "AAAA\n"
+ ["\377"].pack('m') # => "/w==\n"
+ ["\377\377"].pack('m') # => "//8=\n"
+ ["\377\377\377"].pack('m') # => "////\n"
+
+ "".unpack('m') # => [""]
+ "AA==\n".unpack('m') # => ["\x00"]
+ "AAA=\n".unpack('m') # => ["\x00\x00"]
+ "AAAA\n".unpack('m') # => ["\x00\x00\x00"]
+ "/w==\n".unpack('m') # => ["\xFF"]
+ "//8=\n".unpack('m') # => ["\xFF\xFF"]
+ "////\n".unpack('m') # => ["\xFF\xFF\xFF"]
+ "A\n".unpack('m') # => [""]
+ "AA\n".unpack('m') # => ["\x00"]
+ "AA=\n".unpack('m') # => ["\x00"]
+ "AAA\n".unpack('m') # => ["\x00\x00"]
+
+ [""].pack('m0') # => ""
+ ["\0"].pack('m0') # => "AA=="
+ ["\0\0"].pack('m0') # => "AAA="
+ ["\0\0\0"].pack('m0') # => "AAAA"
+ ["\377"].pack('m0') # => "/w=="
+ ["\377\377"].pack('m0') # => "//8="
+ ["\377\377\377"].pack('m0') # => "////"
+
+ "".unpack('m0') # => [""]
+ "AA==".unpack('m0') # => ["\x00"]
+ "AAA=".unpack('m0') # => ["\x00\x00"]
+ "AAAA".unpack('m0') # => ["\x00\x00\x00"]
+ "/w==".unpack('m0') # => ["\xFF"]
+ "//8=".unpack('m0') # => ["\xFF\xFF"]
+ "////".unpack('m0') # => ["\xFF\xFF\xFF"]
+
+- <tt>'u'</tt> - UU-encoded string:
+
+ [0].pack("U") # => "\u0000"
+ [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF"
+ [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80"
+ [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF"
+
+=== Offset Directives
+
+- <tt>'@'</tt> - Begin packing at the given byte offset;
+ for packing, null fill if necessary:
+
+ [1, 2].pack("C@0C") # => "\x02"
+ [1, 2].pack("C@1C") # => "\x01\x02"
+ [1, 2].pack("C@5C") # => "\x01\x00\x00\x00\x00\x02"
+
+ "\x01\x00\x00\x02".unpack("C@3C") # => [1, 2]
+ "\x00".unpack("@1C") # => [nil]
+
+- <tt>'X'</tt> - Back up a byte:
+
+ [0, 1, 2].pack("CCXC") # => "\x00\x02"
+ [0, 1, 2].pack("CCX2C") # => "\x02"
+ "\x00\x02".unpack("CCXC") # => [0, 2, 2]
+
+=== Null Byte Direcive
+
+- <tt>'x'</tt> - Null byte:
+
+ [].pack("x0") # => ""
+ [].pack("x") # => "\x00"
+ [].pack("x8") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x02".unpack("CxC") # => [0, 2]
diff --git a/doc/regexp.rdoc b/doc/regexp.rdoc
index f3844d5729..92c7ecf66e 100644
--- a/doc/regexp.rdoc
+++ b/doc/regexp.rdoc
@@ -28,7 +28,7 @@ Specifically, <tt>/st/</tt> requires that the string contains the letter
_s_ followed by the letter _t_, so it matches _haystack_, also.
Note that any Regexp matching will raise a RuntimeError if timeout is set and
-exceeded. See "Timeout" section in detail.
+exceeded. See {"Timeout"}[#label-Timeout] section in detail.
== \Regexp Interpolation
@@ -550,12 +550,16 @@ characters, <i>anchoring</i> the match to a specific position.
* <tt>(?<!</tt><i>pat</i><tt>)</tt> - <i>Negative lookbehind</i>
assertion: ensures that the preceding characters do not match
<i>pat</i>, but doesn't include those characters in the matched text
-* <tt>\K</tt> - Uses an positive lookbehind of the content preceding
- <tt>\K</tt> in the regexp. For example, the following two regexps are
- almost equivalent:
- /ab\Kc/
- /(?<=ab)c/
+* <tt>\K</tt> - <i>Match reset</i>: the matched content preceding
+ <tt>\K</tt> in the regexp is excluded from the result. For example,
+ the following two regexps are almost equivalent:
+
+ /ab\Kc/ =~ "abc" #=> 0
+ /(?<=ab)c/ =~ "abc" #=> 2
+
+ These match same string and <i>$&</i> equals <tt>"c"</tt>, while the
+ matched position is different.
As are the following two regexps:
@@ -777,7 +781,7 @@ with <i>a{0,29}</i>:
== Timeout
-There are two APIs to set timeout. One is Timeout.timeout=, which is
+There are two APIs to set timeout. One is Regexp.timeout=, which is
process-global configuration of timeout for Regexp matching.
Regexp.timeout = 3
@@ -792,6 +796,6 @@ The other is timeout keyword of Regexp.new.
When using Regexps to process untrusted input, you should use the timeout
feature to avoid excessive backtracking. Otherwise, a malicious user can
-provide input to Regexp causing Denail-of-Service attack.
+provide input to Regexp causing Denial-of-Service attack.
Note that the timeout is not set by default because an appropriate limit
highly depends on an application requirement and context.
diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc
index 5e10e6a140..b641433249 100644
--- a/doc/syntax/literals.rdoc
+++ b/doc/syntax/literals.rdoc
@@ -277,6 +277,12 @@ the content. Note that empty lines and lines consisting solely of literal tabs
and spaces will be ignored for the purposes of determining indentation, but
escaped tabs and spaces are considered non-indentation characters.
+For the purpose of measuring an indentation, a horizontal tab is regarded as a
+sequence of one to eight spaces such that the column position corresponding to
+its end is a multiple of eight. The amount to be removed is counted in terms
+of the number of spaces. If the boundary appears in the middle of a tab, that
+tab is not removed.
+
A heredoc allows interpolation and escaped characters. You may disable
interpolation and escaping by surrounding the opening identifier with single
quotes:
diff --git a/doc/time/in.rdoc b/doc/time/in.rdoc
deleted file mode 100644
index 506bd91628..0000000000
--- a/doc/time/in.rdoc
+++ /dev/null
@@ -1,4 +0,0 @@
-- <tt>in: zone</tt>: a timezone +zone+.
-
-For forms of argument +zone+, see
-{Timezone Specifiers}[rdoc-ref:timezone_specifiers.rdoc].
diff --git a/doc/time/mon-min.rdoc b/doc/time/mon-min.rdoc
deleted file mode 100644
index 5bd430c74a..0000000000
--- a/doc/time/mon-min.rdoc
+++ /dev/null
@@ -1,8 +0,0 @@
-- +month+: a month value, which may be:
- - An integer month in the range <tt>1..12</tt>.
- - A 3-character string that matches regular expression
- <tt>/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/i</tt>.
-- +day+: an integer day in the range <tt>1..31</tt>
- (less than 31 for some months).
-- +hour+: an integer hour in the range <tt>0..23</tt>.
-- +min+: an integer minute in the range <tt>0..59</tt>.
diff --git a/doc/time/msec.rdoc b/doc/time/msec.rdoc
deleted file mode 100644
index ce5d1e6145..0000000000
--- a/doc/time/msec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +msec+ is the number of milliseconds (Integer, Float, or Rational)
- in the range <tt>0..1000</tt>.
diff --git a/doc/time/nsec.rdoc b/doc/time/nsec.rdoc
deleted file mode 100644
index a2dfe2d608..0000000000
--- a/doc/time/nsec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +nsec+ is the number of nanoseconds (Integer, Float, or Rational)
- in the range <tt>0..1000000000</tt>.
diff --git a/doc/time/sec.rdoc b/doc/time/sec.rdoc
deleted file mode 100644
index 049c712110..0000000000
--- a/doc/time/sec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +sec+ is the number of seconds (Integer, Float, or Rational)
- in the range <tt>0..60</tt>.
diff --git a/doc/time/sec_i.rdoc b/doc/time/sec_i.rdoc
deleted file mode 100644
index fd5519082c..0000000000
--- a/doc/time/sec_i.rdoc
+++ /dev/null
@@ -1 +0,0 @@
-- +sec_i+ is the integer number of seconds in the range <tt>0..60</tt>.
diff --git a/doc/time/usec.rdoc b/doc/time/usec.rdoc
deleted file mode 100644
index bb5a46419a..0000000000
--- a/doc/time/usec.rdoc
+++ /dev/null
@@ -1,2 +0,0 @@
-- +usec+ is the number of microseconds (Integer, Float, or Rational)
- in the range <tt>0..1000000</tt>.
diff --git a/doc/time/year.rdoc b/doc/time/year.rdoc
deleted file mode 100644
index 2222b830d7..0000000000
--- a/doc/time/year.rdoc
+++ /dev/null
@@ -1 +0,0 @@
-- +year+: an integer year.
diff --git a/doc/time/zone_and_in.rdoc b/doc/time/zone_and_in.rdoc
deleted file mode 100644
index f36940ee13..0000000000
--- a/doc/time/zone_and_in.rdoc
+++ /dev/null
@@ -1,5 +0,0 @@
-- +zone+: a timezone +zone+.
-- <tt>in: zone</tt>: a timezone +zone+.
-
-For forms of +zone+, see
-{Timezone Specifiers}[rdoc-ref:timezone_specifiers.rdoc].
diff --git a/doc/timezone_specifiers.rdoc b/doc/timezone_specifiers.rdoc
deleted file mode 100644
index a6d57a1b21..0000000000
--- a/doc/timezone_specifiers.rdoc
+++ /dev/null
@@ -1,46 +0,0 @@
-=== Timezone Specifiers
-
-Certain methods in class Time accept arguments that specify timezones:
-
-- Time.at: keyword argument +in:+.
-- Time.new: positional argument +zone+ or keyword argument +in:+.
-- Time.now: keyword argument +in:+.
-- Time#getlocal: positional argument +zone+.
-- Time#localtime: positional argument +zone+.
-
-The value given with any of these must be one of the following:
-
-- A string offset from UTC in the form <tt>'+HH:MM'</tt> or <tt>-HH:MM</tt>,
- where:
-
- - +HH+ is the 2-digit hour in the range <tt>0..23</tt>.
- - +MM+ is the 2-digit minute in the range <tt>0..59</tt>.
-
- Examples:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359
- Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
-
-- A letter in the range <tt>'A'..'I'</tt> or <tt>'K'..'Z'</tt>;
- see {List of military time zones}[https://en.wikipedia.org/wiki/List_of_military_time_zones]:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100
- Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900
- Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000
- Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200
- Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
-
-- An integer number of seconds in the range <tt>-86399..86399</tt>:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959
- Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
-
---
-TODO: Pull in and revise the text at the link,
-then add the example class TZ from the tests.
-++
-- A timezone object;
- see {Timezone Argument}[rdoc-ref:Time@Timezone+Argument] for details.
diff --git a/doc/timezones.rdoc b/doc/timezones.rdoc
new file mode 100644
index 0000000000..c3aae88fde
--- /dev/null
+++ b/doc/timezones.rdoc
@@ -0,0 +1,108 @@
+== Timezones
+
+=== Timezone Specifiers
+
+Certain \Time methods accept arguments that specify timezones:
+
+- Time.at: keyword argument +in:+.
+- Time.new: positional argument +zone+ or keyword argument +in:+.
+- Time.now: keyword argument +in:+.
+- Time#getlocal: positional argument +zone+.
+- Time#localtime: positional argument +zone+.
+
+The value given with any of these must be one of the following
+(each detailed below):
+
+- {Hours/minutes offset}[rdoc-ref:timezones.rdoc@Hours-2FMinutes+Offsets].
+- {Single-letter offset}[rdoc-ref:timezones.rdoc@Single-Letter+Offsets].
+- {Integer offset}[rdoc-ref:timezones.rdoc@Integer+Offsets].
+- {Timezone object}[rdoc-ref:timezones.rdoc@Timezone+Objects].
+
+==== Hours/Minutes Offsets
+
+The zone value may be a string offset from UTC
+in the form <tt>'+HH:MM'</tt> or <tt>'-HH:MM'</tt>,
+where:
+
+- +HH+ is the 2-digit hour in the range <tt>0..23</tt>.
+- +MM+ is the 2-digit minute in the range <tt>0..59</tt>.
+
+Examples:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359
+ Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
+
+==== Single-Letter Offsets
+
+The zone value may be a letter in the range <tt>'A'..'I'</tt>
+or <tt>'K'..'Z'</tt>;
+see {List of military time zones}[https://en.wikipedia.org/wiki/List_of_military_time_zones]:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100
+ Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900
+ Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000
+ Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200
+ Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
+
+==== \Integer Offsets
+
+The zone value may be an integer number of seconds
+in the range <tt>-86399..86399</tt>:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959
+ Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
+
+==== Timezone Objects
+
+In most cases, the zone value may be an object
+responding to certain timezone methods.
+
+\Exceptions (timezone object not allowed):
+
+- Time.new with positional argument +zone+.
+- Time.now with keyword argument +in:+.
+
+The timezone methods are:
+
+- +local_to_utc+:
+
+ - Called when Time.new is invoked with +tz+
+ as the value of positional argument +zone+ or keyword argument +in:+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a \Time-like object in the UTC timezone.
+
+- +utc_to_local+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a \Time-like object in the local timezone.
+
+A custom timezone class may have these instance methods,
+which will be called if defined:
+
+- +abbr+:
+
+ - Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: a string abbreviation for the timezone name.
+
+- +dst?+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a <tt>Time::tm</tt> object.
+ - Returns: whether the time is daylight saving time.
+
+- +name+:
+
+ - Called when <tt>Marshal.dump(t) is invoked
+ - Argument: none.
+ - Returns: the string name of the timezone.
diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md
index f879e227ad..67b2ffa5f0 100644
--- a/doc/yjit/yjit.md
+++ b/doc/yjit/yjit.md
@@ -8,46 +8,54 @@
YJIT - Yet Another Ruby JIT
===========================
-**DISCLAIMER: Please note that this project is experimental. It is very much a work in progress, it may cause your software to crash, and current performance results will vary widely, especially on larger applications.**
-
YJIT is a lightweight, minimalistic Ruby JIT built inside CRuby.
-It lazily compiles code using a Basic Block Versioning (BBV) architecture. The target use case is that of servers running
-Ruby on Rails, an area where MJIT has not yet managed to deliver speedups.
-To simplify development, we currently support only macOS and Linux on x86-64, but an ARM64 backend
-is part of future plans.
+It lazily compiles code using a Basic Block Versioning (BBV) architecture.
+The target use case is that of servers running Ruby on Rails.
+YJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs.
This project is open source and falls under the same license as CRuby.
+<p align="center"><b>
+ If you're using YJIT in production, please
+ <a href="mailto:maxime.chevalierboisvert@shopify.com">share your success stories with us!</a>
+ </b></p>
+
If you wish to learn more about the approach taken, here are some conference talks and publications:
+- RubyKaigi 2022 keynote: [Stories from developing YJIT](https://www.youtube.com/watch?v=EMchdR9C8XM)
+- RubyKaigi 2022 talk: [Building a Lightweight IR and Backend for YJIT](https://www.youtube.com/watch?v=BbLGqTxTRp0)
- RubyKaigi 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=PBVLf3yfMs8)
- Blog post: [YJIT: Building a New JIT Compiler Inside CRuby](https://pointersgonewild.com/2021/06/02/yjit-building-a-new-jit-compiler-inside-cruby/)
+- VMIL 2021 paper: [YJIT: A Basic Block Versioning JIT Compiler for CRuby](https://dl.acm.org/doi/10.1145/3486606.3486781)
- MoreVMs 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=vucLAqv7qpc)
- ECOOP 2016 talk: [Interprocedural Type Specialization of JavaScript Programs Without Type Analysis](https://www.youtube.com/watch?v=sRNBY7Ss97A)
- ECOOP 2016 paper: [Interprocedural Type Specialization of JavaScript Programs Without Type Analysis](https://drops.dagstuhl.de/opus/volltexte/2016/6101/pdf/LIPIcs-ECOOP-2016-7.pdf)
- ECOOP 2015 talk: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://www.youtube.com/watch?v=S-aHBuoiYE0)
- ECOOP 2015 paper: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://arxiv.org/pdf/1411.0352.pdf)
-To cite this repository in your publications, please use this bibtex snippet:
-
-```
-@misc{yjit_ruby_jit,
- author = {Chevalier-Boisvert, Maxime and Wu, Alan and Patterson, Aaron},
- title = {YJIT - Yet Another Ruby JIT},
- year = {2021},
- publisher = {GitHub},
- journal = {GitHub repository},
- howpublished = {\url{https://github.com/Shopify/yjit}},
+To cite YJIT in your publications, please cite the VMIL 2021 paper:
+
+```
+@inproceedings{yjit_vmil2021,
+author = {Chevalier-Boisvert, Maxime and Gibbs, Noah and Boussier, Jean and Wu, Si Xing (Alan) and Patterson, Aaron and Newton, Kevin and Hawthorn, John},
+title = {YJIT: A Basic Block Versioning JIT Compiler for CRuby},
+year = {2021},
+isbn = {9781450391092},
+publisher = {Association for Computing Machinery},
+address = {New York, NY, USA},
+url = {https://doi.org/10.1145/3486606.3486781},
+doi = {10.1145/3486606.3486781},
+booktitle = {Proceedings of the 13th ACM SIGPLAN International Workshop on Virtual Machines and Intermediate Languages},
+pages = {25–32},
+numpages = {8},
+keywords = {ruby, dynamically typed, compiler, optimization, just-in-time, bytecode},
+location = {Chicago, IL, USA},
+series = {VMIL 2021}
}
```
## Current Limitations
-YJIT is a work in progress and as such may not yet be mature enough for mission-critical software. Below is a list of known limitations, all of which we plan to eventually address:
-
-- No garbage collection for generated code.
-- Currently supports only macOS and Linux.
-- Currently supports only x86-64 CPUs.
-
-Because there is no GC for generated code yet, your software could run out of executable memory if it is large enough. You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options).
+YJIT may not be suitable for certain applications. It currently only supports macOS and Linux on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information.
+You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options). There is a slight performance tradeoff because allocating less executable memory could result in the generated machine code being collected more often.
## Installation
@@ -57,6 +65,7 @@ You will need to install:
- A C compiler such as GCC or Clang
- GNU Make and Autoconf
- The Rust compiler `rustc` and Cargo (if you want to build in dev/debug mode)
+ - The Rust version must be [>= 1.58.0](../../yjit/Cargo.toml).
To install the Rust build toolchain, we suggest following the [recommended installation method][rust-install]. Rust also provides first class [support][editor-tools] for many source code editors.
@@ -67,51 +76,62 @@ To install the Rust build toolchain, we suggest following the [recommended insta
Start by cloning the `ruby/ruby` repository:
-```
+```sh
git clone https://github.com/ruby/ruby yjit
cd yjit
```
-The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-compile-and-install).
+The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-build).
-```
+```sh
# Configure in release mode for maximum performance, build and install
./autogen.sh
-./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc
-make -j install
+./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
or
+```sh
+# Configure in lower-performance dev (debug) mode for development, build and install
+./autogen.sh
+./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
-# Configure in dev (debug) mode for development, build and install
+
+Dev mode includes extended YJIT statistics, but can be slow. For only statistics you can configure in stats mode:
+
+```sh
+# Configure in extended-stats mode without slow runtime checks, build and install
./autogen.sh
-./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc
-make -j install
+./configure --enable-yjit=stats --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
+make -j && make install
```
On macOS, you may need to specify where to find some libraries:
-```
+```sh
# Install dependencies
brew install openssl readline libyaml
# Configure in dev (debug) mode for development, build and install
./autogen.sh
-./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)"
-make -j install
+./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)"
+make -j && make install
```
Typically configure will choose the default C compiler. To specify the C compiler, use
-```
+
+```sh
# Choosing a specific c compiler
export CC=/path/to/my/chosen/c/compiler
```
+
before running `./configure`.
You can test that YJIT works correctly by running:
-```
+```sh
# Quick tests found in /bootstraptest
make btest
@@ -126,71 +146,111 @@ make -j test-all
Once YJIT is built, you can either use `./miniruby` from within your build directory, or switch to the YJIT version of `ruby`
by using the `chruby` tool:
-```
+```sh
chruby ruby-yjit
ruby myscript.rb
```
You can dump statistics about compilation and execution by running YJIT with the `--yjit-stats` command-line option:
-```
+```sh
./miniruby --yjit-stats myscript.rb
```
The machine code generated for a given method can be printed by adding `puts RubyVM::YJIT.disasm(method(:method_name))` to a Ruby script. Note that no code will be generated if the method is not compiled.
-
### Command-Line Options
YJIT supports all command-line options supported by upstream CRuby, but also adds a few YJIT-specific options:
- `--yjit`: enable YJIT (disabled by default)
-- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 2)
-- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 256 MiB)
-- `--yjit-stats`: produce statistics after the execution of a program (must compile with `cppflags=-DRUBY_DEBUG` to use this)
-- `--yjit-max-versions=N`: maximum number of versions to generate per basic block (default 4)
-- `--yjit-greedy-versioning`: greedy versioning mode (disabled by default, may increase code size)
+- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 30)
+- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 64 MiB)
+- `--yjit-stats`: print statistics after the execution of a program (incurs a run-time cost)
+- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats`
+
+Note that there is also an environment variable `RUBY_YJIT_ENABLE` which can be used to enable YJIT.
+This can be useful for some deployment scripts where specifying an extra command-line option to Ruby is not practical.
### Benchmarking
We have collected a set of benchmarks and implemented a simple benchmarking harness in the [yjit-bench](https://github.com/Shopify/yjit-bench) repository. This benchmarking harness is designed to disable CPU frequency scaling, set process affinity and disable address space randomization so that the variance between benchmarking runs will be as small as possible. Please kindly note that we are at an early stage in this project.
-### Performance Tips
+## Performance Tips for Production Deployments
+
+While YJIT options default to what we think would work well for most workloads,
+they might not necessarily be the best configuration for your application.
+
+This section covers tips on improving YJIT performance in case YJIT does not
+speed up your application in production.
+
+### Increasing --yjit-exec-mem-size
+
+When JIT code size (`RubyVM::YJIT.runtime_stats[:code_region_size]`) reaches this value,
+YJIT triggers "code GC" that frees all JIT code and starts recompiling everything.
+Compiling code takes some time, so scheduling code GC too frequently slows down your application.
+Increasing `--yjit-exec-mem-size` may speed up your application if `RubyVM::YJIT.runtime_stats[:code_gc_count]` is not 0 or 1.
+
+### Running workers as long as possible
+
+It's helpful to call the same code as many times as possible before a process restarts.
+If a process is killed too frequently, the time taken for compiling methods may outweigh
+the speedup obtained by compiling them.
+
+You should monitor the number of requests each process has served.
+If you're periodically killing worker processes, e.g. with `unicorn-worker-killer` or `puma_worker_killer`,
+you may want to reduce the killing frequency or increase the limit.
+
+## Saving YJIT Memory Usage
+
+YJIT allocates memory for JIT code and metadata. Enabling YJIT generally results in more memory usage.
-This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase, but you can profile your code using a tool such as [stackprof](https://github.com/tmm1/stackprof) and refactor the specific methods that make up the largest fractions of the execution time.
+This section goes over tips on minimizing YJIT memory usage in case it uses more than your capacity.
-- Use exceptions for error recovery only, not as part of normal control-flow
+### Increasing --yjit-call-threshold
+
+As of Ruby 3.2, `--yjit-call-threshold` defaults to 30. With this default, some applications end up
+compiling methods that are used only during the application boot. Increasing this option may help
+you reduce the size of JIT code and metadata. It's worth trying different values like `--yjit-call-threshold=100`.
+
+Note that increasing the value too much may result in compiling code too late.
+You should monitor how many requests each worker processes before it's restarted. For example,
+if each process only handles 1000 requests, `--yjit-call-threshold=1000` might be too large for your application.
+
+### Decreasing --yjit-exec-mem-size
+
+`--yjit-exec-mem-size` specifies the JIT code size, but YJIT also uses memory for its metadata,
+which often consumes more memory than JIT code. Generally, YJIT adds memory overhead by roughly
+3-4x of `--yjit-exec-mem-size` in production as of Ruby 3.2. You should multiply that by the number
+of worker processes to estimate the worst case memory overhead.
+
+Running code GC adds overhead, but it could be still faster than recovering from a whole process killed by OOM.
+
+## Code Optimization Tips
+
+This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase. You should ideally start by profiling your application using a tool such as [stackprof](https://github.com/tmm1/stackprof) so that you can determine which methods make up most of the execution time. You can then refactor the specific methods that make up the largest fractions of the execution time. We do not recommend modifying your entire codebase based on the current limitations of YJIT.
+
+- Avoid using `OpenStruct`
- Avoid redefining basic integer operations (i.e. +, -, <, >, etc.)
- Avoid redefining the meaning of `nil`, equality, etc.
- Avoid allocating objects in the hot parts of your code
-- Use while loops if you can, instead of `integer.times`
- Minimize layers of indirection
- Avoid classes that wrap objects if you can
- Avoid methods that just call another method, trivial one liner methods
-- CRuby method calls are costly. Favor larger methods over smaller methods.
- Try to write code so that the same variables always have the same type
+- Use `while` loops if you can, instead of C methods like `Array#each`
+ - This is not idiomatic Ruby, but could help in hot methods
+- CRuby method calls are costly. Avoid things such as methods that only return a value from a hash or return a constant.
-You can also compile YJIT in debug mode and use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
-
-### Memory Statistics
-
-YJIT, including in production configuration, keeps track of the size of generated code. If you check YJIT.runtime_stats you can see them:
-
-```
-$ RUBYOPT="--yjit" irb
-irb(main):001:0> RubyVM::YJIT.runtime_stats
-=> {:inline_code_size=>331945, :outlined_code_size=>272980}
-```
-
-These are the size in bytes of generated inlined code and generated outlined code. If the combined sizes for generated code are very close to the total YJIT exec-mem-size (see above), YJIT will stop generating code once the limit is reached. Try to make sure you have enough exec-mem-size for the program you're running. By default YJIT will allocate 268,435,456 bytes (256 MiB) of space for generated inlined and outlined code.
+You can also use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
### Other Statistics
-If you compile Ruby with RUBY_DEBUG and/or YJIT_STATS defined and run with "--yjit --yjit-stats", YJIT will track and return performance statistics in RubyVM::YJIT.runtime_stats.
+If you run `ruby` with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`.
-```
+```rb
$ RUBYOPT="--yjit --yjit-stats" irb
-irb(main):001:0> YJIT.runtime_stats
+irb(main):001:0> RubyVM::YJIT.runtime_stats
=>
{:inline_code_size=>340745,
:outlined_code_size=>297664,
@@ -204,16 +264,24 @@ irb(main):001:0> YJIT.runtime_stats
Some of the counters include:
-:exec_instruction - how many Ruby bytecode instructions have been executed
-:binding_allocations - number of bindings allocated
-:binding_set - number of variables set via a binding
-:vm_insns_count - number of instructions executed by the Ruby interpreter
-:compiled_iseq_count - number of bytecode sequences compiled
+* :exec_instruction - how many Ruby bytecode instructions have been executed
+* :binding_allocations - number of bindings allocated
+* :binding_set - number of variables set via a binding
+* :code_gc_count - number of garbage collections of compiled code since process start
+* :vm_insns_count - number of instructions executed by the Ruby interpreter
+* :compiled_iseq_count - number of bytecode sequences compiled
+* :inline_code_size - size in bytes of compiled YJIT blocks
+* :outline_code_size - size in bytes of YJIT error-handling compiled code
+* :side_exit_count - number of side exits taken at runtime
+* :total_exit_count - number of exits, including side exits, taken at runtime
+* :avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter
Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.) See yjit_hacking.md for more details.
Performance counter names are not guaranteed to remain the same between Ruby versions. If you're curious what one does, it's usually best to search the source code for it &mdash; but it may change in a later Ruby version.
+The printed text after a --yjit-stats run includes other information that may be named differently than the information in runtime_stats.
+
## Contributing
We welcome open source contributors. You should feel free to open new issues to report bugs or just to ask questions.
@@ -241,8 +309,6 @@ The YJIT source code is divided between:
- `yjit/src/options.rs`: handling of command-line options
- `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen
- `yjit/src/cruby.rs`: C bindings manually exposed to the Rust codebase
-- `misc/test_yjit_asm.sh`: script to compile and run the in-memory assembler tests
-- `misc/yjit_asm_tests.c`: tests for the in-memory assembler
The core of CRuby's interpreter logic is found in:
- `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`)
@@ -273,49 +339,49 @@ There are 3 test suites:
The tests can be run in parallel like this:
-```
+```sh
make -j test-all RUN_OPTS="--yjit-call-threshold=1"
```
Or single-threaded like this, to more easily identify which specific test is failing:
-```
+```sh
make test-all TESTOPTS=--verbose RUN_OPTS="--yjit-call-threshold=1"
```
To debug a single test in `test-all`:
-```
+```sh
make test-all TESTS='test/-ext-/marshal/test_usrmarshal.rb' RUNRUBYOPT=--debugger=lldb RUN_OPTS="--yjit-call-threshold=1"
```
You can also run one specific test in `btest`:
-```
+```sh
make btest BTESTS=bootstraptest/test_ractor.rb RUN_OPTS="--yjit-call-threshold=1"
```
There are shortcuts to run/debug your own test/repro in `test.rb`:
-```
+```sh
make run # runs ./miniruby test.rb
make lldb # launches ./miniruby test.rb in lldb
```
You can use the Intel syntax for disassembly in LLDB, keeping it consistent with YJIT's disassembly:
-```
+```sh
echo "settings set target.x86-disassembly-flavor intel" >> ~/.lldbinit
```
-## Running YJIT on M1
+## Running x86 YJIT on Apple's Rosetta
-It is possible to run YJIT on an Apple M1 via Rosetta. You can find basic
+For development purposes, it is possible to run x86 YJIT on an Apple M1 via Rosetta. You can find basic
instructions below, but there are a few caveats listed further down.
First, install Rosetta:
-```
+```sh
$ softwareupdate --install-rosetta
```
@@ -323,13 +389,13 @@ Now any command can be run with Rosetta via the `arch` command line tool.
Then you can start your shell in an x86 environment:
-```
+```sh
$ arch -x86_64 zsh
```
You can double check your current architecture via the `arch` command:
-```
+```sh
$ arch -x86_64 zsh
$ arch
i386
@@ -337,13 +403,13 @@ i386
You may need to set the default target for `rustc` to x86-64, e.g.
-```
+```sh
$ rustup default stable-x86_64-apple-darwin
```
While in your i386 shell, install Cargo and Homebrew, then hack away!
-### M1 Caveats
+### Rosetta Caveats
1. You must install a version of Homebrew for each architecture
2. Cargo will install in $HOME/.cargo by default, and I don't know a good way to change architectures after install
diff --git a/enc/Makefile.in b/enc/Makefile.in
index dd8ca1b528..9d0c367134 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -51,7 +51,7 @@ optflags = @optflags@
debugflags = @debugflags@
warnflags = @warnflags@
CCDLFLAGS = @CCDLFLAGS@
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir) @incflags@
DEFS = @DEFS@
CPPFLAGS = @CPPFLAGS@ -DONIG_ENC_REGISTER=rb_enc_register
LDFLAGS = @LDFLAGS@
diff --git a/enc/cesu_8.c b/enc/cesu_8.c
index decbb928f4..75f62df280 100644
--- a/enc/cesu_8.c
+++ b/enc/cesu_8.c
@@ -42,6 +42,8 @@
#define VALID_CODE_LIMIT 0x0010ffff
#define utf8_islead(c) ((UChar )((c) & 0xc0) != 0x80)
+#define utf16_is_high_surrogate(v) ((v >> 10) == 0x36)
+#define utf16_is_low_surrogate(v) ((v >> 10) == 0x37)
static const int EncLen_CESU8[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -283,6 +285,12 @@ is_mbc_newline(const UChar* p, const UChar* end, OnigEncoding enc)
return 0;
}
+static int
+utf8_decode_3byte_sequence(const UChar* p)
+{
+ return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+}
+
static OnigCodePoint
mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
{
@@ -295,11 +303,11 @@ mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
case 2:
return ((p[0] & 0x1F) << 6) | (p[1] & 0x3f);
case 3:
- return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+ return utf8_decode_3byte_sequence(p);
case 6:
{
- int high = ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
- int low = ((p[3] & 0xF) << 12) | ((p[4] & 0x3f) << 6) | (p[5] & 0x3f);
+ int high = utf8_decode_3byte_sequence(p);
+ int low = utf8_decode_3byte_sequence(p + 3);
return ((high & 0x03ff) << 10) + (low & 0x03ff) + 0x10000;
}
}
@@ -410,7 +418,6 @@ get_ctype_code_range(OnigCtype ctype, OnigCodePoint *sb_out,
return onigenc_unicode_ctype_code_range(ctype, ranges);
}
-
static UChar*
left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, OnigEncoding enc ARG_UNUSED)
{
@@ -420,6 +427,14 @@ left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, Onig
p = s;
while (!utf8_islead(*p) && p > start) p--;
+
+ if (p > start && s - p == 2 && utf16_is_low_surrogate(utf8_decode_3byte_sequence(p))) {
+ const UChar *p_surrogate_pair = p - 1;
+ while (!utf8_islead(*p_surrogate_pair) && p_surrogate_pair > start) p_surrogate_pair--;
+ if (p - p_surrogate_pair == 3 && utf16_is_high_surrogate(utf8_decode_3byte_sequence(p_surrogate_pair))) {
+ return (UChar* )p_surrogate_pair;
+ }
+ }
return (UChar* )p;
}
diff --git a/enc/depend b/enc/depend
index 60c5a3ebb2..973ad93010 100644
--- a/enc/depend
+++ b/enc/depend
@@ -7018,6 +7018,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h
enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h
+enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h
enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h
enc/trans/iso2022.$(OBJEXT): internal/attr/restrict.h
diff --git a/enc/encdb.c b/enc/encdb.c
index a1936df804..8247e9ff6a 100644
--- a/enc/encdb.c
+++ b/enc/encdb.c
@@ -17,7 +17,7 @@
#define ENC_DEFINE(name) rb_encdb_declare(name)
#define ENC_SET_BASE(name, orig) rb_enc_set_base((name), (orig))
#define ENC_SET_DUMMY(name, orig) rb_enc_set_dummy(name)
-#define ENC_DUMMY_UNICODE(name) rb_encdb_set_unicode(rb_enc_set_dummy(ENC_REPLICATE((name), name "BE")))
+#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name)
void
Init_encdb(void)
diff --git a/enc/jis/props.h.blt b/enc/jis/props.h.blt
index 54aa94f8bc..508a084449 100644
--- a/enc/jis/props.h.blt
+++ b/enc/jis/props.h.blt
@@ -69,7 +69,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*const char *str, unsigned int len*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
#line 43 "enc/jis/props.kwd"
struct enc_property;
@@ -82,7 +82,7 @@ struct enc_property;
#ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
+static const unsigned char gperf_downcase[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
diff --git a/enc/jis/props.kwd b/enc/jis/props.kwd
index 659cf0aff4..9606828459 100644
--- a/enc/jis/props.kwd
+++ b/enc/jis/props.kwd
@@ -37,7 +37,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
%}
struct enc_property;
diff --git a/enc/jis/props.src b/enc/jis/props.src
index 659cf0aff4..9606828459 100644
--- a/enc/jis/props.src
+++ b/enc/jis/props.src
@@ -37,7 +37,7 @@ struct enc_property {
unsigned char ctype;
};
-static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
+static const struct enc_property *onig_jis_property(register const char *str, register size_t len);
%}
struct enc_property;
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index bc0597e3f4..fcfc2c9267 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -134,7 +134,7 @@ else
end
mkin = File.read(File.join($srcdir, "Makefile.in"))
mkin.gsub!(/@(#{CONFIG.keys.join('|')})@/) {CONFIG[$1]}
-open(ARGV[0], 'wb') {|f|
+File.open(ARGV[0], 'wb') {|f|
f.puts mkin, dep
}
if MODULE_TYPE == :static
diff --git a/enc/unicode/14.0.0/casefold.h b/enc/unicode/15.0.0/casefold.h
index d387cff628..51120d867d 100644
--- a/enc/unicode/14.0.0/casefold.h
+++ b/enc/unicode/15.0.0/casefold.h
@@ -1,15 +1,15 @@
/* DO NOT EDIT THIS FILE. */
-/* Generated by enc/unicode/case-folding.rb */
+/* Generated by enc-case-folding.rb */
#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_VERSION_MAJOR == 15 && \
ONIG_UNICODE_VERSION_MINOR == 0 && \
ONIG_UNICODE_VERSION_TEENY == 0 && \
1)
# error ONIG_UNICODE_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_VERSION_STRING "14.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 14
+#define ONIG_UNICODE_VERSION_STRING "15.0.0"
+#define ONIG_UNICODE_VERSION_MAJOR 15
#define ONIG_UNICODE_VERSION_MINOR 0
#define ONIG_UNICODE_VERSION_TEENY 0
diff --git a/enc/unicode/14.0.0/name2ctype.h b/enc/unicode/15.0.0/name2ctype.h
index 99a3eeca19..a2c996423d 100644
--- a/enc/unicode/14.0.0/name2ctype.h
+++ b/enc/unicode/15.0.0/name2ctype.h
@@ -43,7 +43,7 @@ static const OnigCodePoint CR_NEWLINE[] = {
/* 'Alpha': [[:Alpha:]] */
static const OnigCodePoint CR_Alpha[] = {
- 722,
+ 732,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -178,8 +178,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0bca, 0x0bcc,
0x0bd0, 0x0bd0,
0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
+ 0x0c00, 0x0c0c,
0x0c0e, 0x0c10,
0x0c12, 0x0c28,
0x0c2a, 0x0c39,
@@ -202,7 +201,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0cd5, 0x0cd6,
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -240,7 +239,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x0f00, 0x0f00,
0x0f40, 0x0f47,
0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f88, 0x0f97,
0x0f99, 0x0fbc,
0x1000, 0x1036,
@@ -542,7 +541,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x10fe0, 0x10ff6,
0x11000, 0x11045,
0x11071, 0x11075,
- 0x11082, 0x110b8,
+ 0x11080, 0x110b8,
0x110c2, 0x110c2,
0x110d0, 0x110e8,
0x11100, 0x11132,
@@ -557,7 +556,7 @@ static const OnigCodePoint CR_Alpha[] = {
0x11200, 0x11211,
0x11213, 0x11234,
0x11237, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -637,12 +636,16 @@ static const OnigCodePoint CR_Alpha[] = {
0x11d93, 0x11d96,
0x11d98, 0x11d98,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f40,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -666,7 +669,9 @@ static const OnigCodePoint CR_Alpha[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -705,16 +710,20 @@ static const OnigCodePoint CR_Alpha[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -760,12 +769,13 @@ static const OnigCodePoint CR_Alpha[] = {
0x1f150, 0x1f169,
0x1f170, 0x1f189,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Alpha */
/* 'Blank': [[:Blank:]] */
@@ -790,7 +800,7 @@ static const OnigCodePoint CR_Cntrl[] = {
/* 'Digit': [[:Digit:]] */
static const OnigCodePoint CR_Digit[] = {
- 62,
+ 64,
0x0030, 0x0039,
0x0660, 0x0669,
0x06f0, 0x06f9,
@@ -845,19 +855,21 @@ static const OnigCodePoint CR_Digit[] = {
0x11c50, 0x11c59,
0x11d50, 0x11d59,
0x11da0, 0x11da9,
+ 0x11f50, 0x11f59,
0x16a60, 0x16a69,
0x16ac0, 0x16ac9,
0x16b50, 0x16b59,
0x1d7ce, 0x1d7ff,
0x1e140, 0x1e149,
0x1e2f0, 0x1e2f9,
+ 0x1e4f0, 0x1e4f9,
0x1e950, 0x1e959,
0x1fbf0, 0x1fbf9,
}; /* CR_Digit */
/* 'Graph': [[:Graph:]] */
static const OnigCodePoint CR_Graph[] = {
- 703,
+ 712,
0x0021, 0x007e,
0x00a1, 0x0377,
0x037a, 0x037f,
@@ -980,7 +992,7 @@ static const OnigCodePoint CR_Graph[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -1010,7 +1022,7 @@ static const OnigCodePoint CR_Graph[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -1285,7 +1297,7 @@ static const OnigCodePoint CR_Graph[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -1302,7 +1314,7 @@ static const OnigCodePoint CR_Graph[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -1355,6 +1367,7 @@ static const OnigCodePoint CR_Graph[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -1376,6 +1389,9 @@ static const OnigCodePoint CR_Graph[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -1383,8 +1399,7 @@ static const OnigCodePoint CR_Graph[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -1411,7 +1426,9 @@ static const OnigCodePoint CR_Graph[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -1426,6 +1443,7 @@ static const OnigCodePoint CR_Graph[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -1453,11 +1471,14 @@ static const OnigCodePoint CR_Graph[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -1465,6 +1486,7 @@ static const OnigCodePoint CR_Graph[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -1523,10 +1545,10 @@ static const OnigCodePoint CR_Graph[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -1537,25 +1559,24 @@ static const OnigCodePoint CR_Graph[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -1565,7 +1586,7 @@ static const OnigCodePoint CR_Graph[] = {
/* 'Lower': [[:Lower:]] */
static const OnigCodePoint CR_Lower[] = {
- 664,
+ 671,
0x0061, 0x007a,
0x00aa, 0x00aa,
0x00b5, 0x00b5,
@@ -1842,7 +1863,7 @@ static const OnigCodePoint CR_Lower[] = {
0x052f, 0x052f,
0x0560, 0x0588,
0x10d0, 0x10fa,
- 0x10fd, 0x10ff,
+ 0x10fc, 0x10ff,
0x13f8, 0x13fd,
0x1c80, 0x1c88,
0x1d00, 0x1dbf,
@@ -2182,10 +2203,11 @@ static const OnigCodePoint CR_Lower[] = {
0xa7d5, 0xa7d5,
0xa7d7, 0xa7d7,
0xa7d9, 0xa7d9,
+ 0xa7f2, 0xa7f4,
0xa7f6, 0xa7f6,
0xa7f8, 0xa7fa,
0xab30, 0xab5a,
- 0xab5c, 0xab68,
+ 0xab5c, 0xab69,
0xab70, 0xabbf,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -2196,6 +2218,10 @@ static const OnigCodePoint CR_Lower[] = {
0x105a3, 0x105b1,
0x105b3, 0x105b9,
0x105bb, 0x105bc,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
0x10cc0, 0x10cf2,
0x118c0, 0x118df,
0x16e60, 0x16e7f,
@@ -2229,12 +2255,14 @@ static const OnigCodePoint CR_Lower[] = {
0x1d7cb, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e922, 0x1e943,
}; /* CR_Lower */
/* 'Print': [[:Print:]] */
static const OnigCodePoint CR_Print[] = {
- 700,
+ 709,
0x0020, 0x007e,
0x00a0, 0x0377,
0x037a, 0x037f,
@@ -2357,7 +2385,7 @@ static const OnigCodePoint CR_Print[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -2387,7 +2415,7 @@ static const OnigCodePoint CR_Print[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -2659,7 +2687,7 @@ static const OnigCodePoint CR_Print[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -2676,7 +2704,7 @@ static const OnigCodePoint CR_Print[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -2729,6 +2757,7 @@ static const OnigCodePoint CR_Print[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -2750,6 +2779,9 @@ static const OnigCodePoint CR_Print[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -2757,8 +2789,7 @@ static const OnigCodePoint CR_Print[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -2785,7 +2816,9 @@ static const OnigCodePoint CR_Print[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -2800,6 +2833,7 @@ static const OnigCodePoint CR_Print[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -2827,11 +2861,14 @@ static const OnigCodePoint CR_Print[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -2839,6 +2876,7 @@ static const OnigCodePoint CR_Print[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -2897,10 +2935,10 @@ static const OnigCodePoint CR_Print[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -2911,25 +2949,24 @@ static const OnigCodePoint CR_Print[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -2939,7 +2976,7 @@ static const OnigCodePoint CR_Print[] = {
/* 'XPosixPunct': [[:Punct:]] */
static const OnigCodePoint CR_XPosixPunct[] = {
- 184,
+ 186,
0x0021, 0x002f,
0x003a, 0x0040,
0x005b, 0x0060,
@@ -3109,9 +3146,11 @@ static const OnigCodePoint CR_XPosixPunct[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -3807,7 +3846,7 @@ static const OnigCodePoint CR_XDigit[] = {
/* 'Word': [[:Word:]] */
static const OnigCodePoint CR_Word[] = {
- 758,
+ 770,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -3965,7 +4004,7 @@ static const OnigCodePoint CR_Word[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -3998,7 +4037,7 @@ static const OnigCodePoint CR_Word[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -4311,7 +4350,7 @@ static const OnigCodePoint CR_Word[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -4334,7 +4373,7 @@ static const OnigCodePoint CR_Word[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -4415,12 +4454,17 @@ static const OnigCodePoint CR_Word[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -4448,7 +4492,9 @@ static const OnigCodePoint CR_Word[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -4502,17 +4548,21 @@ static const OnigCodePoint CR_Word[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -4559,18 +4609,19 @@ static const OnigCodePoint CR_Word[] = {
0x1f170, 0x1f189,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_Word */
/* 'Alnum': [[:Alnum:]] */
static const OnigCodePoint CR_Alnum[] = {
- 760,
+ 772,
0x0030, 0x0039,
0x0041, 0x005a,
0x0061, 0x007a,
@@ -4709,8 +4760,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0bd0, 0x0bd0,
0x0bd7, 0x0bd7,
0x0be6, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
+ 0x0c00, 0x0c0c,
0x0c0e, 0x0c10,
0x0c12, 0x0c28,
0x0c2a, 0x0c39,
@@ -4735,7 +4785,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -4778,7 +4828,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x0f20, 0x0f29,
0x0f40, 0x0f47,
0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f88, 0x0f97,
0x0f99, 0x0fbc,
0x1000, 0x1036,
@@ -5088,7 +5138,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x11000, 0x11045,
0x11066, 0x1106f,
0x11071, 0x11075,
- 0x11082, 0x110b8,
+ 0x11080, 0x110b8,
0x110c2, 0x110c2,
0x110d0, 0x110e8,
0x110f0, 0x110f9,
@@ -5104,7 +5154,7 @@ static const OnigCodePoint CR_Alnum[] = {
0x11200, 0x11211,
0x11213, 0x11234,
0x11237, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -5194,12 +5244,17 @@ static const OnigCodePoint CR_Alnum[] = {
0x11d98, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f40,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -5226,7 +5281,9 @@ static const OnigCodePoint CR_Alnum[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -5266,11 +5323,14 @@ static const OnigCodePoint CR_Alnum[] = {
0x1d7c4, 0x1d7cb,
0x1d7ce, 0x1d7ff,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e140, 0x1e149,
@@ -5278,6 +5338,8 @@ static const OnigCodePoint CR_Alnum[] = {
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
0x1e2f0, 0x1e2f9,
+ 0x1e4d0, 0x1e4eb,
+ 0x1e4f0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -5325,12 +5387,13 @@ static const OnigCodePoint CR_Alnum[] = {
0x1f170, 0x1f189,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Alnum */
/* 'ASCII': [[:ASCII:]] */
@@ -5341,7 +5404,7 @@ static const OnigCodePoint CR_ASCII[] = {
/* 'Punct' */
static const OnigCodePoint CR_Punct[] = {
- 189,
+ 191,
0x0021, 0x0023,
0x0025, 0x002a,
0x002c, 0x002f,
@@ -5516,9 +5579,11 @@ static const OnigCodePoint CR_Punct[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -5542,7 +5607,7 @@ static const OnigCodePoint CR_Any[] = {
/* 'Assigned': - */
static const OnigCodePoint CR_Assigned[] = {
- 698,
+ 707,
0x0000, 0x0377,
0x037a, 0x037f,
0x0384, 0x038a,
@@ -5664,7 +5729,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -5694,7 +5759,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f47,
@@ -5965,7 +6030,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10ead,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f27,
+ 0x10efd, 0x10f27,
0x10f30, 0x10f59,
0x10f70, 0x10f89,
0x10fb0, 0x10fcb,
@@ -5982,7 +6047,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x11180, 0x111df,
0x111e1, 0x111f4,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -6035,6 +6100,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x11a00, 0x11a47,
0x11a50, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c36,
0x11c38, 0x11c45,
@@ -6056,6 +6122,9 @@ static const OnigCodePoint CR_Assigned[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -6063,8 +6132,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 0x13000, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -6091,7 +6159,9 @@ static const OnigCodePoint CR_Assigned[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -6106,6 +6176,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x1d100, 0x1d126,
0x1d129, 0x1d1ea,
0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -6133,11 +6204,14 @@ static const OnigCodePoint CR_Assigned[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
@@ -6145,6 +6219,7 @@ static const OnigCodePoint CR_Assigned[] = {
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -6203,10 +6278,10 @@ static const OnigCodePoint CR_Assigned[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -6217,25 +6292,24 @@ static const OnigCodePoint CR_Assigned[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0001, 0xe0001,
0xe0020, 0xe007f,
0xe0100, 0xe01ef,
@@ -6245,7 +6319,7 @@ static const OnigCodePoint CR_Assigned[] = {
/* 'C': Major Category */
static const OnigCodePoint CR_C[] = {
- 701,
+ 712,
0x0000, 0x001f,
0x007f, 0x009f,
0x00ad, 0x00ad,
@@ -6372,7 +6446,7 @@ static const OnigCodePoint CR_C[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -6402,7 +6476,7 @@ static const OnigCodePoint CR_C[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -6674,7 +6748,7 @@ static const OnigCodePoint CR_C[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -6692,7 +6766,7 @@ static const OnigCodePoint CR_C[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -6744,7 +6818,8 @@ static const OnigCodePoint CR_C[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -6765,7 +6840,10 @@ static const OnigCodePoint CR_C[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -6773,7 +6851,8 @@ static const OnigCodePoint CR_C[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x143ff,
+ 0x13430, 0x1343f,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -6799,8 +6878,10 @@ static const OnigCodePoint CR_C[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -6815,7 +6896,8 @@ static const OnigCodePoint CR_C[] = {
0x1d127, 0x1d128,
0x1d173, 0x1d17a,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -6842,19 +6924,23 @@ static const OnigCodePoint CR_C[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -6912,11 +6998,11 @@ static const OnigCodePoint CR_C[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -6927,25 +7013,24 @@ static const OnigCodePoint CR_C[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe00ff,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe00ff,
0xe01f0, 0x10ffff,
}; /* CR_C */
@@ -6971,7 +7056,7 @@ static const OnigCodePoint CR_Cf[] = {
0xfff9, 0xfffb,
0x110bd, 0x110bd,
0x110cd, 0x110cd,
- 0x13430, 0x13438,
+ 0x13430, 0x1343f,
0x1bca0, 0x1bca3,
0x1d173, 0x1d17a,
0xe0001, 0xe0001,
@@ -6980,7 +7065,7 @@ static const OnigCodePoint CR_Cf[] = {
/* 'Cn': General Category */
static const OnigCodePoint CR_Cn[] = {
- 698,
+ 707,
0x0378, 0x0379,
0x0380, 0x0383,
0x038b, 0x038b,
@@ -7102,7 +7187,7 @@ static const OnigCodePoint CR_Cn[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -7132,7 +7217,7 @@ static const OnigCodePoint CR_Cn[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -7402,7 +7487,7 @@ static const OnigCodePoint CR_Cn[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -7420,7 +7505,7 @@ static const OnigCodePoint CR_Cn[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -7472,7 +7557,8 @@ static const OnigCodePoint CR_Cn[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -7493,7 +7579,10 @@ static const OnigCodePoint CR_Cn[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -7501,8 +7590,7 @@ static const OnigCodePoint CR_Cn[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x1342f,
- 0x13439, 0x143ff,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -7528,8 +7616,10 @@ static const OnigCodePoint CR_Cn[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -7543,7 +7633,8 @@ static const OnigCodePoint CR_Cn[] = {
0x1d0f6, 0x1d0ff,
0x1d127, 0x1d128,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -7570,19 +7661,23 @@ static const OnigCodePoint CR_Cn[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -7640,11 +7735,11 @@ static const OnigCodePoint CR_Cn[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -7655,25 +7750,24 @@ static const OnigCodePoint CR_Cn[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe0000,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe0000,
0xe0002, 0xe001f,
0xe0080, 0xe00ff,
0xe01f0, 0xeffff,
@@ -7697,7 +7791,7 @@ static const OnigCodePoint CR_Cs[] = {
/* 'L': Major Category */
static const OnigCodePoint CR_L[] = {
- 648,
+ 659,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -8167,6 +8261,7 @@ static const OnigCodePoint CR_L[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -8229,11 +8324,15 @@ static const OnigCodePoint CR_L[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -8256,7 +8355,9 @@ static const OnigCodePoint CR_L[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -8294,11 +8395,14 @@ static const OnigCodePoint CR_L[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -8340,17 +8444,18 @@ static const OnigCodePoint CR_L[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_L */
/* 'LC': General Category */
static const OnigCodePoint CR_LC[] = {
- 142,
+ 143,
0x0041, 0x005a,
0x0061, 0x007a,
0x00b5, 0x00b5,
@@ -8492,12 +8597,13 @@ static const OnigCodePoint CR_LC[] = {
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e900, 0x1e943,
}; /* CR_LC */
/* 'Ll': General Category */
static const OnigCodePoint CR_Ll[] = {
- 657,
+ 658,
0x0061, 0x007a,
0x00b5, 0x00b5,
0x00df, 0x00f6,
@@ -9154,12 +9260,13 @@ static const OnigCodePoint CR_Ll[] = {
0x1d7cb, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e922, 0x1e943,
}; /* CR_Ll */
/* 'Lm': General Category */
static const OnigCodePoint CR_Lm[] = {
- 69,
+ 71,
0x02b0, 0x02c1,
0x02c6, 0x02d1,
0x02e0, 0x02e4,
@@ -9227,13 +9334,15 @@ static const OnigCodePoint CR_Lm[] = {
0x1aff0, 0x1aff3,
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
+ 0x1e030, 0x1e06d,
0x1e137, 0x1e13d,
+ 0x1e4eb, 0x1e4eb,
0x1e94b, 0x1e94b,
}; /* CR_Lm */
/* 'Lo': General Category */
static const OnigCodePoint CR_Lo[] = {
- 501,
+ 510,
0x00aa, 0x00aa,
0x00ba, 0x00ba,
0x01bb, 0x01bb,
@@ -9598,6 +9707,7 @@ static const OnigCodePoint CR_Lo[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -9659,11 +9769,15 @@ static const OnigCodePoint CR_Lo[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -9678,7 +9792,9 @@ static const OnigCodePoint CR_Lo[] = {
0x18800, 0x18cd5,
0x18d00, 0x18d08,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -9690,6 +9806,7 @@ static const OnigCodePoint CR_Lo[] = {
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4ea,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -9729,12 +9846,13 @@ static const OnigCodePoint CR_Lo[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Lo */
/* 'Lt': General Category */
@@ -10405,7 +10523,7 @@ static const OnigCodePoint CR_Lu[] = {
/* 'M': Major Category */
static const OnigCodePoint CR_M[] = {
- 299,
+ 310,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -10486,6 +10604,7 @@ static const OnigCodePoint CR_M[] = {
0x0cca, 0x0ccd,
0x0cd5, 0x0cd6,
0x0ce2, 0x0ce3,
+ 0x0cf3, 0x0cf3,
0x0d00, 0x0d03,
0x0d3b, 0x0d3c,
0x0d3e, 0x0d44,
@@ -10504,7 +10623,7 @@ static const OnigCodePoint CR_M[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -10606,6 +10725,7 @@ static const OnigCodePoint CR_M[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11000, 0x11002,
@@ -10625,6 +10745,7 @@ static const OnigCodePoint CR_M[] = {
0x111ce, 0x111cf,
0x1122c, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112ea,
0x11300, 0x11303,
0x1133b, 0x1133c,
@@ -10672,6 +10793,12 @@ static const OnigCodePoint CR_M[] = {
0x11d90, 0x11d91,
0x11d93, 0x11d97,
0x11ef3, 0x11ef6,
+ 0x11f00, 0x11f01,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -10699,9 +10826,11 @@ static const OnigCodePoint CR_M[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0100, 0xe01ef,
@@ -10709,7 +10838,7 @@ static const OnigCodePoint CR_M[] = {
/* 'Mc': General Category */
static const OnigCodePoint CR_Mc[] = {
- 177,
+ 182,
0x0903, 0x0903,
0x093b, 0x093b,
0x093e, 0x0940,
@@ -10745,6 +10874,7 @@ static const OnigCodePoint CR_Mc[] = {
0x0cc7, 0x0cc8,
0x0cca, 0x0ccb,
0x0cd5, 0x0cd6,
+ 0x0cf3, 0x0cf3,
0x0d02, 0x0d03,
0x0d3e, 0x0d40,
0x0d46, 0x0d48,
@@ -10883,6 +11013,10 @@ static const OnigCodePoint CR_Mc[] = {
0x11d93, 0x11d94,
0x11d96, 0x11d96,
0x11ef5, 0x11ef6,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
0x16f51, 0x16f87,
0x16ff0, 0x16ff1,
0x1d165, 0x1d166,
@@ -10901,7 +11035,7 @@ static const OnigCodePoint CR_Me[] = {
/* 'Mn': General Category */
static const OnigCodePoint CR_Mn[] = {
- 336,
+ 346,
0x0300, 0x036f,
0x0483, 0x0487,
0x0591, 0x05bd,
@@ -10994,7 +11128,7 @@ static const OnigCodePoint CR_Mn[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -11125,6 +11259,7 @@ static const OnigCodePoint CR_Mn[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -11147,6 +11282,7 @@ static const OnigCodePoint CR_Mn[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -11208,6 +11344,12 @@ static const OnigCodePoint CR_Mn[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -11232,9 +11374,11 @@ static const OnigCodePoint CR_Mn[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0100, 0xe01ef,
@@ -11242,7 +11386,7 @@ static const OnigCodePoint CR_Mn[] = {
/* 'N': Major Category */
static const OnigCodePoint CR_N[] = {
- 134,
+ 137,
0x0030, 0x0039,
0x00b2, 0x00b3,
0x00b9, 0x00b9,
@@ -11356,6 +11500,7 @@ static const OnigCodePoint CR_N[] = {
0x11c50, 0x11c6c,
0x11d50, 0x11d59,
0x11da0, 0x11da9,
+ 0x11f50, 0x11f59,
0x11fc0, 0x11fd4,
0x12400, 0x1246e,
0x16a60, 0x16a69,
@@ -11363,11 +11508,13 @@ static const OnigCodePoint CR_N[] = {
0x16b50, 0x16b59,
0x16b5b, 0x16b61,
0x16e80, 0x16e96,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d360, 0x1d378,
0x1d7ce, 0x1d7ff,
0x1e140, 0x1e149,
0x1e2f0, 0x1e2f9,
+ 0x1e4f0, 0x1e4f9,
0x1e8c7, 0x1e8cf,
0x1e950, 0x1e959,
0x1ec71, 0x1ecab,
@@ -11401,7 +11548,7 @@ static const OnigCodePoint CR_Nl[] = {
/* 'No': General Category */
static const OnigCodePoint CR_No[] = {
- 71,
+ 72,
0x00b2, 0x00b3,
0x00b9, 0x00b9,
0x00bc, 0x00be,
@@ -11464,6 +11611,7 @@ static const OnigCodePoint CR_No[] = {
0x11fc0, 0x11fd4,
0x16b5b, 0x16b61,
0x16e80, 0x16e96,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d360, 0x1d378,
0x1e8c7, 0x1e8cf,
@@ -11627,7 +11775,7 @@ static const OnigCodePoint CR_Pi[] = {
/* 'Po': General Category */
static const OnigCodePoint CR_Po[] = {
- 185,
+ 187,
0x0021, 0x0023,
0x0025, 0x0027,
0x002a, 0x002a,
@@ -11798,9 +11946,11 @@ static const OnigCodePoint CR_Po[] = {
0x11a3f, 0x11a46,
0x11a9a, 0x11a9c,
0x11a9e, 0x11aa2,
+ 0x11b00, 0x11b09,
0x11c41, 0x11c45,
0x11c70, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f4f,
0x11fff, 0x11fff,
0x12470, 0x12474,
0x12ff1, 0x12ff2,
@@ -11901,7 +12051,7 @@ static const OnigCodePoint CR_Ps[] = {
/* 'S': Major Category */
static const OnigCodePoint CR_S[] = {
- 234,
+ 232,
0x0024, 0x0024,
0x002b, 0x002b,
0x003c, 0x003e,
@@ -12111,10 +12261,10 @@ static const OnigCodePoint CR_S[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -12125,15 +12275,13 @@ static const OnigCodePoint CR_S[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
}; /* CR_S */
@@ -12271,7 +12419,7 @@ static const OnigCodePoint CR_Sm[] = {
/* 'So': General Category */
static const OnigCodePoint CR_So[] = {
- 186,
+ 184,
0x00a6, 0x00a6,
0x00a9, 0x00a9,
0x00ae, 0x00ae,
@@ -12433,10 +12581,10 @@ static const OnigCodePoint CR_So[] = {
0x1f260, 0x1f265,
0x1f300, 0x1f3fa,
0x1f400, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -12447,15 +12595,13 @@ static const OnigCodePoint CR_So[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
}; /* CR_So */
@@ -12651,7 +12797,7 @@ static const OnigCodePoint CR_Math[] = {
/* 'Cased': Derived Property */
static const OnigCodePoint CR_Cased[] = {
- 151,
+ 157,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -12683,7 +12829,7 @@ static const OnigCodePoint CR_Cased[] = {
0x10c7, 0x10c7,
0x10cd, 0x10cd,
0x10d0, 0x10fa,
- 0x10fd, 0x10ff,
+ 0x10fc, 0x10ff,
0x13a0, 0x13f5,
0x13f8, 0x13fd,
0x1c80, 0x1c88,
@@ -12743,10 +12889,10 @@ static const OnigCodePoint CR_Cased[] = {
0xa7d0, 0xa7d1,
0xa7d3, 0xa7d3,
0xa7d5, 0xa7d9,
- 0xa7f5, 0xa7f6,
+ 0xa7f2, 0xa7f6,
0xa7f8, 0xa7fa,
0xab30, 0xab5a,
- 0xab5c, 0xab68,
+ 0xab5c, 0xab69,
0xab70, 0xabbf,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -12763,6 +12909,10 @@ static const OnigCodePoint CR_Cased[] = {
0x105a3, 0x105b1,
0x105b3, 0x105b9,
0x105bb, 0x105bc,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
0x10c80, 0x10cb2,
0x10cc0, 0x10cf2,
0x118a0, 0x118df,
@@ -12799,6 +12949,8 @@ static const OnigCodePoint CR_Cased[] = {
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df09,
0x1df0b, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e900, 0x1e943,
0x1f130, 0x1f149,
0x1f150, 0x1f169,
@@ -12807,7 +12959,7 @@ static const OnigCodePoint CR_Cased[] = {
/* 'Case_Ignorable': Derived Property */
static const OnigCodePoint CR_Case_Ignorable[] = {
- 427,
+ 437,
0x0027, 0x0027,
0x002e, 0x002e,
0x003a, 0x003a,
@@ -12921,7 +13073,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -13110,6 +13262,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -13134,6 +13287,7 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -13195,7 +13349,12 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
- 0x13430, 0x13438,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13430, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16b40, 0x16b43,
@@ -13226,9 +13385,12 @@ static const OnigCodePoint CR_Case_Ignorable[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e13d,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4eb, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94b,
0x1f3fb, 0x1f3ff,
@@ -15879,7 +16041,7 @@ static const OnigCodePoint CR_Changes_When_Casemapped[] = {
/* 'ID_Start': Derived Property */
static const OnigCodePoint CR_ID_Start[] = {
- 648,
+ 659,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -16348,6 +16510,7 @@ static const OnigCodePoint CR_ID_Start[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -16410,12 +16573,16 @@ static const OnigCodePoint CR_ID_Start[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -16438,7 +16605,9 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -16476,11 +16645,14 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -16522,17 +16694,18 @@ static const OnigCodePoint CR_ID_Start[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_ID_Start */
/* 'ID_Continue': Derived Property */
static const OnigCodePoint CR_ID_Continue[] = {
- 756,
+ 768,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -16691,7 +16864,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -16724,7 +16897,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -17037,7 +17210,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -17060,7 +17233,7 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -17141,12 +17314,17 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -17174,7 +17352,9 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -17228,17 +17408,21 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -17282,18 +17466,19 @@ static const OnigCodePoint CR_ID_Continue[] = {
0x1eeab, 0x1eebb,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_ID_Continue */
/* 'XID_Start': Derived Property */
static const OnigCodePoint CR_XID_Start[] = {
- 655,
+ 666,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -17769,6 +17954,7 @@ static const OnigCodePoint CR_XID_Start[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x1122b,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -17831,12 +18017,16 @@ static const OnigCodePoint CR_XID_Start[] = {
0x11d6a, 0x11d89,
0x11d98, 0x11d98,
0x11ee0, 0x11ef2,
+ 0x11f02, 0x11f02,
+ 0x11f04, 0x11f10,
+ 0x11f12, 0x11f33,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -17859,7 +18049,9 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -17897,11 +18089,14 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1d7aa, 0x1d7c2,
0x1d7c4, 0x1d7cb,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ad,
0x1e2c0, 0x1e2eb,
+ 0x1e4d0, 0x1e4eb,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -17943,17 +18138,18 @@ static const OnigCodePoint CR_XID_Start[] = {
0x1eea5, 0x1eea9,
0x1eeab, 0x1eebb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_XID_Start */
/* 'XID_Continue': Derived Property */
static const OnigCodePoint CR_XID_Continue[] = {
- 763,
+ 775,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@@ -18112,7 +18308,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d00, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d44,
@@ -18145,7 +18341,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
0x0f00, 0x0f00,
@@ -18465,7 +18661,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x10e80, 0x10ea9,
0x10eab, 0x10eac,
0x10eb0, 0x10eb1,
- 0x10f00, 0x10f1c,
+ 0x10efd, 0x10f1c,
0x10f27, 0x10f27,
0x10f30, 0x10f50,
0x10f70, 0x10f85,
@@ -18488,7 +18684,7 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x111dc, 0x111dc,
0x11200, 0x11211,
0x11213, 0x11237,
- 0x1123e, 0x1123e,
+ 0x1123e, 0x11241,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -18569,12 +18765,17 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x11d93, 0x11d98,
0x11da0, 0x11da9,
0x11ee0, 0x11ef6,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f42,
+ 0x11f50, 0x11f59,
0x11fb0, 0x11fb0,
0x12000, 0x12399,
0x12400, 0x1246e,
0x12480, 0x12543,
0x12f90, 0x12ff0,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13440, 0x13455,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -18602,7 +18803,9 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -18656,17 +18859,21 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1da9b, 0x1da9f,
0x1daa1, 0x1daaf,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
0x1e000, 0x1e006,
0x1e008, 0x1e018,
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
0x1e100, 0x1e12c,
0x1e130, 0x1e13d,
0x1e140, 0x1e149,
0x1e14e, 0x1e14e,
0x1e290, 0x1e2ae,
0x1e2c0, 0x1e2f9,
+ 0x1e4d0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -18710,12 +18917,13 @@ static const OnigCodePoint CR_XID_Continue[] = {
0x1eeab, 0x1eebb,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
0xe0100, 0xe01ef,
}; /* CR_XID_Continue */
@@ -18743,7 +18951,7 @@ static const OnigCodePoint CR_Default_Ignorable_Code_Point[] = {
/* 'Grapheme_Extend': Derived Property */
static const OnigCodePoint CR_Grapheme_Extend[] = {
- 353,
+ 363,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -18846,7 +19054,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -18975,6 +19183,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -18997,6 +19206,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -19064,6 +19274,12 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -19090,9 +19306,11 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0xe0020, 0xe007f,
@@ -19101,7 +19319,7 @@ static const OnigCodePoint CR_Grapheme_Extend[] = {
/* 'Grapheme_Base': Derived Property */
static const OnigCodePoint CR_Grapheme_Base[] = {
- 861,
+ 875,
0x0020, 0x007e,
0x00a0, 0x00ac,
0x00ae, 0x02ff,
@@ -19251,7 +19469,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce1,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
0x0d02, 0x0d0c,
0x0d0e, 0x0d10,
0x0d12, 0x0d3a,
@@ -19670,6 +19888,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11232, 0x11233,
0x11235, 0x11235,
0x11238, 0x1123d,
+ 0x1123f, 0x11240,
0x11280, 0x11286,
0x11288, 0x11288,
0x1128a, 0x1128d,
@@ -19756,6 +19975,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11a97, 0x11a97,
0x11a9a, 0x11aa2,
0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
0x11c00, 0x11c08,
0x11c0a, 0x11c2f,
0x11c3e, 0x11c3e,
@@ -19779,6 +19999,11 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x11da0, 0x11da9,
0x11ee0, 0x11ef2,
0x11ef5, 0x11ef8,
+ 0x11f02, 0x11f10,
+ 0x11f12, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
+ 0x11f43, 0x11f59,
0x11fb0, 0x11fb0,
0x11fc0, 0x11ff1,
0x11fff, 0x12399,
@@ -19786,7 +20011,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x12470, 0x12474,
0x12480, 0x12543,
0x12f90, 0x12ff2,
- 0x13000, 0x1342e,
+ 0x13000, 0x1342f,
+ 0x13441, 0x13446,
0x14400, 0x14646,
0x16800, 0x16a38,
0x16a40, 0x16a5e,
@@ -19814,7 +20040,9 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1aff5, 0x1affb,
0x1affd, 0x1affe,
0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
0x1b170, 0x1b2fb,
0x1bc00, 0x1bc6a,
@@ -19834,6 +20062,7 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1d1ae, 0x1d1ea,
0x1d200, 0x1d241,
0x1d245, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -19863,6 +20092,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1da76, 0x1da83,
0x1da85, 0x1da8b,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e030, 0x1e06d,
0x1e100, 0x1e12c,
0x1e137, 0x1e13d,
0x1e140, 0x1e149,
@@ -19871,6 +20102,8 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1e2c0, 0x1e2eb,
0x1e2f0, 0x1e2f9,
0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4eb,
+ 0x1e4f0, 0x1e4f9,
0x1e7e0, 0x1e7e6,
0x1e7e8, 0x1e7eb,
0x1e7ed, 0x1e7ee,
@@ -19930,10 +20163,10 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -19944,30 +20177,29 @@ static const OnigCodePoint CR_Grapheme_Base[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Grapheme_Base */
/* 'Grapheme_Link': Derived Property */
static const OnigCodePoint CR_Grapheme_Link[] = {
- 55,
+ 56,
0x094d, 0x094d,
0x09cd, 0x09cd,
0x0a4d, 0x0a4d,
@@ -20023,11 +20255,12 @@ static const OnigCodePoint CR_Grapheme_Link[] = {
0x11c3f, 0x11c3f,
0x11d44, 0x11d45,
0x11d97, 0x11d97,
+ 0x11f41, 0x11f42,
}; /* CR_Grapheme_Link */
/* 'Common': Script */
static const OnigCodePoint CR_Common[] = {
- 174,
+ 173,
0x0000, 0x0040,
0x005b, 0x0060,
0x007b, 0x00a9,
@@ -20134,6 +20367,7 @@ static const OnigCodePoint CR_Common[] = {
0x1d183, 0x1d184,
0x1d18c, 0x1d1a9,
0x1d1ae, 0x1d1ea,
+ 0x1d2c0, 0x1d2d3,
0x1d2e0, 0x1d2f3,
0x1d300, 0x1d356,
0x1d360, 0x1d378,
@@ -20174,10 +20408,10 @@ static const OnigCodePoint CR_Common[] = {
0x1f250, 0x1f251,
0x1f260, 0x1f265,
0x1f300, 0x1f6d7,
- 0x1f6dd, 0x1f6ec,
+ 0x1f6dc, 0x1f6ec,
0x1f6f0, 0x1f6fc,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d8,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
0x1f7e0, 0x1f7eb,
0x1f7f0, 0x1f7f0,
0x1f800, 0x1f80b,
@@ -20188,15 +20422,13 @@ static const OnigCodePoint CR_Common[] = {
0x1f8b0, 0x1f8b1,
0x1f900, 0x1fa53,
0x1fa60, 0x1fa6d,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
0x1fb00, 0x1fb92,
0x1fb94, 0x1fbca,
0x1fbf0, 0x1fbf9,
@@ -20206,7 +20438,7 @@ static const OnigCodePoint CR_Common[] = {
/* 'Latin': Script */
static const OnigCodePoint CR_Latin[] = {
- 38,
+ 39,
0x0041, 0x005a,
0x0061, 0x007a,
0x00aa, 0x00aa,
@@ -20245,6 +20477,7 @@ static const OnigCodePoint CR_Latin[] = {
0x10787, 0x107b0,
0x107b2, 0x107ba,
0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
}; /* CR_Latin */
/* 'Greek': Script */
@@ -20290,7 +20523,7 @@ static const OnigCodePoint CR_Greek[] = {
/* 'Cyrillic': Script */
static const OnigCodePoint CR_Cyrillic[] = {
- 8,
+ 10,
0x0400, 0x0484,
0x0487, 0x052f,
0x1c80, 0x1c88,
@@ -20299,6 +20532,8 @@ static const OnigCodePoint CR_Cyrillic[] = {
0x2de0, 0x2dff,
0xa640, 0xa69f,
0xfe2e, 0xfe2f,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
}; /* CR_Cyrillic */
/* 'Armenian': Script */
@@ -20326,7 +20561,7 @@ static const OnigCodePoint CR_Hebrew[] = {
/* 'Arabic': Script */
static const OnigCodePoint CR_Arabic[] = {
- 57,
+ 58,
0x0600, 0x0604,
0x0606, 0x060b,
0x060d, 0x061a,
@@ -20350,6 +20585,7 @@ static const OnigCodePoint CR_Arabic[] = {
0xfe70, 0xfe74,
0xfe76, 0xfefc,
0x10e60, 0x10e7e,
+ 0x10efd, 0x10eff,
0x1ee00, 0x1ee03,
0x1ee05, 0x1ee1f,
0x1ee21, 0x1ee22,
@@ -20403,11 +20639,12 @@ static const OnigCodePoint CR_Thaana[] = {
/* 'Devanagari': Script */
static const OnigCodePoint CR_Devanagari[] = {
- 4,
+ 5,
0x0900, 0x0950,
0x0955, 0x0963,
0x0966, 0x097f,
0xa8e0, 0xa8ff,
+ 0x11b00, 0x11b09,
}; /* CR_Devanagari */
/* 'Bengali': Script */
@@ -20544,7 +20781,7 @@ static const OnigCodePoint CR_Kannada[] = {
0x0cdd, 0x0cde,
0x0ce0, 0x0ce3,
0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
+ 0x0cf1, 0x0cf3,
}; /* CR_Kannada */
/* 'Malayalam': Script */
@@ -20595,7 +20832,7 @@ static const OnigCodePoint CR_Lao[] = {
0x0ea7, 0x0ebd,
0x0ec0, 0x0ec4,
0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0ed0, 0x0ed9,
0x0edc, 0x0edf,
}; /* CR_Lao */
@@ -20746,17 +20983,18 @@ static const OnigCodePoint CR_Mongolian[] = {
/* 'Hiragana': Script */
static const OnigCodePoint CR_Hiragana[] = {
- 5,
+ 6,
0x3041, 0x3096,
0x309d, 0x309f,
0x1b001, 0x1b11f,
+ 0x1b132, 0x1b132,
0x1b150, 0x1b152,
0x1f200, 0x1f200,
}; /* CR_Hiragana */
/* 'Katakana': Script */
static const OnigCodePoint CR_Katakana[] = {
- 13,
+ 14,
0x30a1, 0x30fa,
0x30fd, 0x30ff,
0x31f0, 0x31ff,
@@ -20769,6 +21007,7 @@ static const OnigCodePoint CR_Katakana[] = {
0x1affd, 0x1affe,
0x1b000, 0x1b000,
0x1b120, 0x1b122,
+ 0x1b155, 0x1b155,
0x1b164, 0x1b167,
}; /* CR_Katakana */
@@ -20782,7 +21021,7 @@ static const OnigCodePoint CR_Bopomofo[] = {
/* 'Han': Script */
static const OnigCodePoint CR_Han[] = {
- 20,
+ 21,
0x2e80, 0x2e99,
0x2e9b, 0x2ef3,
0x2f00, 0x2fd5,
@@ -20797,12 +21036,13 @@ static const OnigCodePoint CR_Han[] = {
0x16fe2, 0x16fe3,
0x16ff0, 0x16ff1,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Han */
/* 'Yi': Script */
@@ -21165,9 +21405,8 @@ static const OnigCodePoint CR_Avestan[] = {
/* 'Egyptian_Hieroglyphs': Script */
static const OnigCodePoint CR_Egyptian_Hieroglyphs[] = {
- 2,
- 0x13000, 0x1342e,
- 0x13430, 0x13438,
+ 1,
+ 0x13000, 0x13455,
}; /* CR_Egyptian_Hieroglyphs */
/* 'Samaritan': Script */
@@ -21382,7 +21621,7 @@ static const OnigCodePoint CR_Pahawh_Hmong[] = {
static const OnigCodePoint CR_Khojki[] = {
2,
0x11200, 0x11211,
- 0x11213, 0x1123e,
+ 0x11213, 0x11241,
}; /* CR_Khojki */
/* 'Linear_A': Script */
@@ -21772,6 +22011,20 @@ static const OnigCodePoint CR_Vithkuqi[] = {
0x105bb, 0x105bc,
}; /* CR_Vithkuqi */
+/* 'Kawi': Script */
+static const OnigCodePoint CR_Kawi[] = {
+ 3,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
+}; /* CR_Kawi */
+
+/* 'Nag_Mundari': Script */
+static const OnigCodePoint CR_Nag_Mundari[] = {
+ 1,
+ 0x1e4d0, 0x1e4f9,
+}; /* CR_Nag_Mundari */
+
/* 'White_Space': Binary Property */
#define CR_White_Space CR_Space
@@ -21853,7 +22106,7 @@ static const OnigCodePoint CR_Quotation_Mark[] = {
/* 'Terminal_Punctuation': Binary Property */
static const OnigCodePoint CR_Terminal_Punctuation[] = {
- 107,
+ 108,
0x0021, 0x0021,
0x002c, 0x002c,
0x002e, 0x002e,
@@ -21953,6 +22206,7 @@ static const OnigCodePoint CR_Terminal_Punctuation[] = {
0x11c41, 0x11c43,
0x11c71, 0x11c71,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f44,
0x12470, 0x12474,
0x16a6e, 0x16a6f,
0x16af5, 0x16af5,
@@ -22118,7 +22372,7 @@ static const OnigCodePoint CR_Hex_Digit[] = {
/* 'Other_Alphabetic': Binary Property */
static const OnigCodePoint CR_Other_Alphabetic[] = {
- 233,
+ 240,
0x0345, 0x0345,
0x05b0, 0x05bd,
0x05bf, 0x05bf,
@@ -22178,7 +22432,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0bc6, 0x0bc8,
0x0bca, 0x0bcc,
0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
+ 0x0c00, 0x0c04,
0x0c3e, 0x0c44,
0x0c46, 0x0c48,
0x0c4a, 0x0c4c,
@@ -22190,6 +22444,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0cca, 0x0ccc,
0x0cd5, 0x0cd6,
0x0ce2, 0x0ce3,
+ 0x0cf3, 0x0cf3,
0x0d00, 0x0d03,
0x0d3e, 0x0d44,
0x0d46, 0x0d48,
@@ -22208,7 +22463,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x0eb4, 0x0eb9,
0x0ebb, 0x0ebc,
0x0ecd, 0x0ecd,
- 0x0f71, 0x0f81,
+ 0x0f71, 0x0f83,
0x0f8d, 0x0f97,
0x0f99, 0x0fbc,
0x102b, 0x1036,
@@ -22281,7 +22536,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x11000, 0x11002,
0x11038, 0x11045,
0x11073, 0x11074,
- 0x11082, 0x11082,
+ 0x11080, 0x11082,
0x110b0, 0x110b8,
0x110c2, 0x110c2,
0x11100, 0x11102,
@@ -22293,6 +22548,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x1122c, 0x11234,
0x11237, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112e8,
0x11300, 0x11303,
0x1133e, 0x11344,
@@ -22338,6 +22594,10 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x11d90, 0x11d91,
0x11d93, 0x11d96,
0x11ef3, 0x11ef6,
+ 0x11f00, 0x11f01,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f3a,
+ 0x11f3e, 0x11f40,
0x16f4f, 0x16f4f,
0x16f51, 0x16f87,
0x16f8f, 0x16f92,
@@ -22348,6 +22608,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e947, 0x1e947,
0x1f130, 0x1f149,
0x1f150, 0x1f169,
@@ -22356,7 +22617,7 @@ static const OnigCodePoint CR_Other_Alphabetic[] = {
/* 'Ideographic': Binary Property */
static const OnigCodePoint CR_Ideographic[] = {
- 19,
+ 20,
0x3006, 0x3007,
0x3021, 0x3029,
0x3038, 0x303a,
@@ -22370,17 +22631,18 @@ static const OnigCodePoint CR_Ideographic[] = {
0x18d00, 0x18d08,
0x1b170, 0x1b2fb,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x2f800, 0x2fa1d,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Ideographic */
/* 'Diacritic': Binary Property */
static const OnigCodePoint CR_Diacritic[] = {
- 192,
+ 195,
0x005e, 0x005e,
0x0060, 0x0060,
0x00a8, 0x00a8,
@@ -22520,6 +22782,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x107b2, 0x107ba,
0x10ae5, 0x10ae6,
0x10d22, 0x10d27,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11046, 0x11046,
@@ -22553,6 +22816,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x11d42, 0x11d42,
0x11d44, 0x11d45,
0x11d97, 0x11d97,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f8f, 0x16f9f,
@@ -22567,6 +22831,7 @@ static const OnigCodePoint CR_Diacritic[] = {
0x1d17b, 0x1d182,
0x1d185, 0x1d18b,
0x1d1aa, 0x1d1ad,
+ 0x1e030, 0x1e06d,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
@@ -22615,7 +22880,7 @@ static const OnigCodePoint CR_Extender[] = {
/* 'Other_Lowercase': Binary Property */
static const OnigCodePoint CR_Other_Lowercase[] = {
- 20,
+ 28,
0x00aa, 0x00aa,
0x00ba, 0x00ba,
0x02b0, 0x02b8,
@@ -22623,6 +22888,7 @@ static const OnigCodePoint CR_Other_Lowercase[] = {
0x02e0, 0x02e4,
0x0345, 0x0345,
0x037a, 0x037a,
+ 0x10fc, 0x10fc,
0x1d2c, 0x1d6a,
0x1d78, 0x1d78,
0x1d9b, 0x1dbf,
@@ -22634,8 +22900,15 @@ static const OnigCodePoint CR_Other_Lowercase[] = {
0x2c7c, 0x2c7d,
0xa69c, 0xa69d,
0xa770, 0xa770,
+ 0xa7f2, 0xa7f4,
0xa7f8, 0xa7f9,
0xab5c, 0xab5f,
+ 0xab69, 0xab69,
+ 0x10780, 0x10780,
+ 0x10783, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
+ 0x1e030, 0x1e06d,
}; /* CR_Other_Lowercase */
/* 'Other_Uppercase': Binary Property */
@@ -22724,7 +22997,7 @@ static const OnigCodePoint CR_Radical[] = {
/* 'Unified_Ideograph': Binary Property */
static const OnigCodePoint CR_Unified_Ideograph[] = {
- 15,
+ 16,
0x3400, 0x4dbf,
0x4e00, 0x9fff,
0xfa0e, 0xfa0f,
@@ -22735,11 +23008,12 @@ static const OnigCodePoint CR_Unified_Ideograph[] = {
0xfa23, 0xfa24,
0xfa27, 0xfa29,
0x20000, 0x2a6df,
- 0x2a700, 0x2b738,
+ 0x2a700, 0x2b739,
0x2b740, 0x2b81d,
0x2b820, 0x2cea1,
0x2ceb0, 0x2ebe0,
0x30000, 0x3134a,
+ 0x31350, 0x323af,
}; /* CR_Unified_Ideograph */
/* 'Other_Default_Ignorable_Code_Point': Binary Property */
@@ -22773,7 +23047,7 @@ static const OnigCodePoint CR_Deprecated[] = {
/* 'Soft_Dotted': Binary Property */
static const OnigCodePoint CR_Soft_Dotted[] = {
- 32,
+ 34,
0x0069, 0x006a,
0x012f, 0x012f,
0x0249, 0x0249,
@@ -22806,6 +23080,8 @@ static const OnigCodePoint CR_Soft_Dotted[] = {
0x1d65e, 0x1d65f,
0x1d692, 0x1d693,
0x1df1a, 0x1df1a,
+ 0x1e04c, 0x1e04d,
+ 0x1e068, 0x1e068,
}; /* CR_Soft_Dotted */
/* 'Logical_Order_Exception': Binary Property */
@@ -22840,7 +23116,7 @@ static const OnigCodePoint CR_Other_ID_Continue[] = {
/* 'Sentence_Terminal': Binary Property */
static const OnigCodePoint CR_Sentence_Terminal[] = {
- 79,
+ 80,
0x0021, 0x0021,
0x002e, 0x002e,
0x003f, 0x003f,
@@ -22913,6 +23189,7 @@ static const OnigCodePoint CR_Sentence_Terminal[] = {
0x11a9b, 0x11a9c,
0x11c41, 0x11c42,
0x11ef7, 0x11ef8,
+ 0x11f43, 0x11f44,
0x16a6e, 0x16a6f,
0x16af5, 0x16af5,
0x16b37, 0x16b38,
@@ -22994,7 +23271,7 @@ static const OnigCodePoint CR_Regional_Indicator[] = {
/* 'Emoji': Emoji */
static const OnigCodePoint CR_Emoji[] = {
- 153,
+ 151,
0x0023, 0x0023,
0x002a, 0x002a,
0x0030, 0x0039,
@@ -23129,7 +23406,7 @@ static const OnigCodePoint CR_Emoji[] = {
0x1f680, 0x1f6c5,
0x1f6cb, 0x1f6d2,
0x1f6d5, 0x1f6d7,
- 0x1f6dd, 0x1f6e5,
+ 0x1f6dc, 0x1f6e5,
0x1f6e9, 0x1f6e9,
0x1f6eb, 0x1f6ec,
0x1f6f0, 0x1f6f0,
@@ -23139,20 +23416,18 @@ static const OnigCodePoint CR_Emoji[] = {
0x1f90c, 0x1f93a,
0x1f93c, 0x1f945,
0x1f947, 0x1f9ff,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji */
/* 'Emoji_Presentation': Emoji */
static const OnigCodePoint CR_Emoji_Presentation[] = {
- 83,
+ 81,
0x231a, 0x231b,
0x23e9, 0x23ec,
0x23f0, 0x23f0,
@@ -23219,7 +23494,7 @@ static const OnigCodePoint CR_Emoji_Presentation[] = {
0x1f6cc, 0x1f6cc,
0x1f6d0, 0x1f6d2,
0x1f6d5, 0x1f6d7,
- 0x1f6dd, 0x1f6df,
+ 0x1f6dc, 0x1f6df,
0x1f6eb, 0x1f6ec,
0x1f6f4, 0x1f6fc,
0x1f7e0, 0x1f7eb,
@@ -23227,15 +23502,13 @@ static const OnigCodePoint CR_Emoji_Presentation[] = {
0x1f90c, 0x1f93a,
0x1f93c, 0x1f945,
0x1f947, 0x1f9ff,
- 0x1fa70, 0x1fa74,
- 0x1fa78, 0x1fa7c,
- 0x1fa80, 0x1fa86,
- 0x1fa90, 0x1faac,
- 0x1fab0, 0x1faba,
- 0x1fac0, 0x1fac5,
- 0x1fad0, 0x1fad9,
- 0x1fae0, 0x1fae7,
- 0x1faf0, 0x1faf6,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji_Presentation */
/* 'Emoji_Modifier': Emoji */
@@ -23286,7 +23559,7 @@ static const OnigCodePoint CR_Emoji_Modifier_Base[] = {
0x1f9cd, 0x1f9cf,
0x1f9d1, 0x1f9dd,
0x1fac3, 0x1fac5,
- 0x1faf0, 0x1faf6,
+ 0x1faf0, 0x1faf8,
}; /* CR_Emoji_Modifier_Base */
/* 'Emoji_Component': Emoji */
@@ -23389,7 +23662,7 @@ static const OnigCodePoint CR_Extended_Pictographic[] = {
/* 'Unknown': Script */
static const OnigCodePoint CR_Unknown[] = {
- 696,
+ 705,
0x0378, 0x0379,
0x0380, 0x0383,
0x038b, 0x038b,
@@ -23511,7 +23784,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x0cdf, 0x0cdf,
0x0ce4, 0x0ce5,
0x0cf0, 0x0cf0,
- 0x0cf3, 0x0cff,
+ 0x0cf4, 0x0cff,
0x0d0d, 0x0d0d,
0x0d11, 0x0d11,
0x0d45, 0x0d45,
@@ -23541,7 +23814,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x0ebe, 0x0ebf,
0x0ec5, 0x0ec5,
0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
+ 0x0ecf, 0x0ecf,
0x0eda, 0x0edb,
0x0ee0, 0x0eff,
0x0f48, 0x0f48,
@@ -23811,7 +24084,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x10e7f, 0x10e7f,
0x10eaa, 0x10eaa,
0x10eae, 0x10eaf,
- 0x10eb2, 0x10eff,
+ 0x10eb2, 0x10efc,
0x10f28, 0x10f2f,
0x10f5a, 0x10f6f,
0x10f8a, 0x10faf,
@@ -23829,7 +24102,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x111e0, 0x111e0,
0x111f5, 0x111ff,
0x11212, 0x11212,
- 0x1123f, 0x1127f,
+ 0x11242, 0x1127f,
0x11287, 0x11287,
0x11289, 0x11289,
0x1128e, 0x1128e,
@@ -23881,7 +24154,8 @@ static const OnigCodePoint CR_Unknown[] = {
0x119e5, 0x119ff,
0x11a48, 0x11a4f,
0x11aa3, 0x11aaf,
- 0x11af9, 0x11bff,
+ 0x11af9, 0x11aff,
+ 0x11b0a, 0x11bff,
0x11c09, 0x11c09,
0x11c37, 0x11c37,
0x11c46, 0x11c4f,
@@ -23902,7 +24176,10 @@ static const OnigCodePoint CR_Unknown[] = {
0x11d92, 0x11d92,
0x11d99, 0x11d9f,
0x11daa, 0x11edf,
- 0x11ef9, 0x11faf,
+ 0x11ef9, 0x11eff,
+ 0x11f11, 0x11f11,
+ 0x11f3b, 0x11f3d,
+ 0x11f5a, 0x11faf,
0x11fb1, 0x11fbf,
0x11ff2, 0x11ffe,
0x1239a, 0x123ff,
@@ -23910,8 +24187,7 @@ static const OnigCodePoint CR_Unknown[] = {
0x12475, 0x1247f,
0x12544, 0x12f8f,
0x12ff3, 0x12fff,
- 0x1342f, 0x1342f,
- 0x13439, 0x143ff,
+ 0x13456, 0x143ff,
0x14647, 0x167ff,
0x16a39, 0x16a3f,
0x16a5f, 0x16a5f,
@@ -23937,8 +24213,10 @@ static const OnigCodePoint CR_Unknown[] = {
0x1aff4, 0x1aff4,
0x1affc, 0x1affc,
0x1afff, 0x1afff,
- 0x1b123, 0x1b14f,
- 0x1b153, 0x1b163,
+ 0x1b123, 0x1b131,
+ 0x1b133, 0x1b14f,
+ 0x1b153, 0x1b154,
+ 0x1b156, 0x1b163,
0x1b168, 0x1b16f,
0x1b2fc, 0x1bbff,
0x1bc6b, 0x1bc6f,
@@ -23952,7 +24230,8 @@ static const OnigCodePoint CR_Unknown[] = {
0x1d0f6, 0x1d0ff,
0x1d127, 0x1d128,
0x1d1eb, 0x1d1ff,
- 0x1d246, 0x1d2df,
+ 0x1d246, 0x1d2bf,
+ 0x1d2d4, 0x1d2df,
0x1d2f4, 0x1d2ff,
0x1d357, 0x1d35f,
0x1d379, 0x1d3ff,
@@ -23979,19 +24258,23 @@ static const OnigCodePoint CR_Unknown[] = {
0x1da8c, 0x1da9a,
0x1daa0, 0x1daa0,
0x1dab0, 0x1deff,
- 0x1df1f, 0x1dfff,
+ 0x1df1f, 0x1df24,
+ 0x1df2b, 0x1dfff,
0x1e007, 0x1e007,
0x1e019, 0x1e01a,
0x1e022, 0x1e022,
0x1e025, 0x1e025,
- 0x1e02b, 0x1e0ff,
+ 0x1e02b, 0x1e02f,
+ 0x1e06e, 0x1e08e,
+ 0x1e090, 0x1e0ff,
0x1e12d, 0x1e12f,
0x1e13e, 0x1e13f,
0x1e14a, 0x1e14d,
0x1e150, 0x1e28f,
0x1e2af, 0x1e2bf,
0x1e2fa, 0x1e2fe,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e4fa, 0x1e7df,
0x1e7e7, 0x1e7e7,
0x1e7ec, 0x1e7ec,
0x1e7ef, 0x1e7ef,
@@ -24049,11 +24332,11 @@ static const OnigCodePoint CR_Unknown[] = {
0x1f249, 0x1f24f,
0x1f252, 0x1f25f,
0x1f266, 0x1f2ff,
- 0x1f6d8, 0x1f6dc,
+ 0x1f6d8, 0x1f6db,
0x1f6ed, 0x1f6ef,
0x1f6fd, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d9, 0x1f7df,
+ 0x1f777, 0x1f77a,
+ 0x1f7da, 0x1f7df,
0x1f7ec, 0x1f7ef,
0x1f7f1, 0x1f7ff,
0x1f80c, 0x1f80f,
@@ -24064,25 +24347,24 @@ static const OnigCodePoint CR_Unknown[] = {
0x1f8b2, 0x1f8ff,
0x1fa54, 0x1fa5f,
0x1fa6e, 0x1fa6f,
- 0x1fa75, 0x1fa77,
0x1fa7d, 0x1fa7f,
- 0x1fa87, 0x1fa8f,
- 0x1faad, 0x1faaf,
- 0x1fabb, 0x1fabf,
- 0x1fac6, 0x1facf,
- 0x1fada, 0x1fadf,
- 0x1fae8, 0x1faef,
- 0x1faf7, 0x1faff,
+ 0x1fa89, 0x1fa8f,
+ 0x1fabe, 0x1fabe,
+ 0x1fac6, 0x1facd,
+ 0x1fadc, 0x1fadf,
+ 0x1fae9, 0x1faef,
+ 0x1faf9, 0x1faff,
0x1fb93, 0x1fb93,
0x1fbcb, 0x1fbef,
0x1fbfa, 0x1ffff,
0x2a6e0, 0x2a6ff,
- 0x2b739, 0x2b73f,
+ 0x2b73a, 0x2b73f,
0x2b81e, 0x2b81f,
0x2cea2, 0x2ceaf,
0x2ebe1, 0x2f7ff,
0x2fa1e, 0x2ffff,
- 0x3134b, 0xe0000,
+ 0x3134b, 0x3134f,
+ 0x323b0, 0xe0000,
0xe0002, 0xe001f,
0xe0080, 0xe00ff,
0xe01f0, 0x10ffff,
@@ -36632,10 +36914,730 @@ static const OnigCodePoint CR_Age_14_0[] = {
0xefffe, 0x10ffff,
}; /* CR_Age_14_0 */
+/* 'Age_15_0': Derived Age 15.0 */
+static const OnigCodePoint CR_Age_15_0[] = {
+ 715,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0600, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x0870, 0x088e,
+ 0x0890, 0x0891,
+ 0x0898, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09ce,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fe,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a76,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae3,
+ 0x0ae6, 0x0af1,
+ 0x0af9, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b55, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b77,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd0, 0x0bd0,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bfa,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3c, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c5d, 0x0c5d,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cdd, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf3,
+ 0x0d00, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d81, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ece,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fda,
+ 0x1000, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12d6,
+ 0x12d8, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x135a,
+ 0x135d, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x1715,
+ 0x171f, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa0, 0x1aad,
+ 0x1ab0, 0x1ace,
+ 0x1b00, 0x1b4c,
+ 0x1b50, 0x1b7e,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fc4,
+ 0x1fc6, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fdd, 0x1fef,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffe,
+ 0x2000, 0x2064,
+ 0x2066, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20c0,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b97, 0x2cf3,
+ 0x2cf9, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d70,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2e5d,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ca,
+ 0xa7d0, 0xa7d1,
+ 0xa7d3, 0xa7d3,
+ 0xa7d5, 0xa7d9,
+ 0xa7f2, 0xa82c,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab6b,
+ 0xab70, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xd800, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc2,
+ 0xfbd3, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdcf, 0xfe19,
+ 0xfe20, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfff9, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019c,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102fb,
+ 0x10300, 0x10323,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1057a,
+ 0x1057c, 0x1058a,
+ 0x1058c, 0x10592,
+ 0x10594, 0x10595,
+ 0x10597, 0x105a1,
+ 0x105a3, 0x105b1,
+ 0x105b3, 0x105b9,
+ 0x105bb, 0x105bc,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10780, 0x10785,
+ 0x10787, 0x107b0,
+ 0x107b2, 0x107ba,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1089e,
+ 0x108a7, 0x108af,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x108fb, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109bc, 0x109cf,
+ 0x109d2, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a48,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a9f,
+ 0x10ac0, 0x10ae6,
+ 0x10aeb, 0x10af6,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b91,
+ 0x10b99, 0x10b9c,
+ 0x10ba9, 0x10baf,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10cfa, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10e60, 0x10e7e,
+ 0x10e80, 0x10ea9,
+ 0x10eab, 0x10ead,
+ 0x10eb0, 0x10eb1,
+ 0x10efd, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10f70, 0x10f89,
+ 0x10fb0, 0x10fcb,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x11075,
+ 0x1107f, 0x110c2,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11147,
+ 0x11150, 0x11176,
+ 0x11180, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x11241,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a9,
+ 0x112b0, 0x112ea,
+ 0x112f0, 0x112f9,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133b, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11400, 0x1145b,
+ 0x1145d, 0x11461,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b9,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x11746,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x11906,
+ 0x11909, 0x11909,
+ 0x1190c, 0x11913,
+ 0x11915, 0x11916,
+ 0x11918, 0x11935,
+ 0x11937, 0x11938,
+ 0x1193b, 0x11946,
+ 0x11950, 0x11959,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ab0, 0x11af8,
+ 0x11b00, 0x11b09,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c45,
+ 0x11c50, 0x11c6c,
+ 0x11c70, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d47,
+ 0x11d50, 0x11d59,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d98,
+ 0x11da0, 0x11da9,
+ 0x11ee0, 0x11ef8,
+ 0x11f00, 0x11f10,
+ 0x11f12, 0x11f3a,
+ 0x11f3e, 0x11f59,
+ 0x11fb0, 0x11fb0,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x12f90, 0x12ff2,
+ 0x13000, 0x13455,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16abe,
+ 0x16ac0, 0x16ac9,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe4,
+ 0x16ff0, 0x16ff1,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18cd5,
+ 0x18d00, 0x18d08,
+ 0x1aff0, 0x1aff3,
+ 0x1aff5, 0x1affb,
+ 0x1affd, 0x1affe,
+ 0x1b000, 0x1b122,
+ 0x1b132, 0x1b132,
+ 0x1b150, 0x1b152,
+ 0x1b155, 0x1b155,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1cf00, 0x1cf2d,
+ 0x1cf30, 0x1cf46,
+ 0x1cf50, 0x1cfc3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1ea,
+ 0x1d200, 0x1d245,
+ 0x1d2c0, 0x1d2d3,
+ 0x1d2e0, 0x1d2f3,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d378,
+ 0x1d400, 0x1d454,
+ 0x1d456, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c3,
+ 0x1d4c5, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d51e, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d552, 0x1d6a5,
+ 0x1d6a8, 0x1d7cb,
+ 0x1d7ce, 0x1da8b,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1df00, 0x1df1e,
+ 0x1df25, 0x1df2a,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e030, 0x1e06d,
+ 0x1e08f, 0x1e08f,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e290, 0x1e2ae,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 0x1e4d0, 0x1e4f9,
+ 0x1e7e0, 0x1e7e6,
+ 0x1e7e8, 0x1e7eb,
+ 0x1e7ed, 0x1e7ee,
+ 0x1e7f0, 0x1e7fe,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+ 0x1ec71, 0x1ecb4,
+ 0x1ed01, 0x1ed3d,
+ 0x1ee00, 0x1ee03,
+ 0x1ee05, 0x1ee1f,
+ 0x1ee21, 0x1ee22,
+ 0x1ee24, 0x1ee24,
+ 0x1ee27, 0x1ee27,
+ 0x1ee29, 0x1ee32,
+ 0x1ee34, 0x1ee37,
+ 0x1ee39, 0x1ee39,
+ 0x1ee3b, 0x1ee3b,
+ 0x1ee42, 0x1ee42,
+ 0x1ee47, 0x1ee47,
+ 0x1ee49, 0x1ee49,
+ 0x1ee4b, 0x1ee4b,
+ 0x1ee4d, 0x1ee4f,
+ 0x1ee51, 0x1ee52,
+ 0x1ee54, 0x1ee54,
+ 0x1ee57, 0x1ee57,
+ 0x1ee59, 0x1ee59,
+ 0x1ee5b, 0x1ee5b,
+ 0x1ee5d, 0x1ee5d,
+ 0x1ee5f, 0x1ee5f,
+ 0x1ee61, 0x1ee62,
+ 0x1ee64, 0x1ee64,
+ 0x1ee67, 0x1ee6a,
+ 0x1ee6c, 0x1ee72,
+ 0x1ee74, 0x1ee77,
+ 0x1ee79, 0x1ee7c,
+ 0x1ee7e, 0x1ee7e,
+ 0x1ee80, 0x1ee89,
+ 0x1ee8b, 0x1ee9b,
+ 0x1eea1, 0x1eea3,
+ 0x1eea5, 0x1eea9,
+ 0x1eeab, 0x1eebb,
+ 0x1eef0, 0x1eef1,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0bf,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0f5,
+ 0x1f100, 0x1f1ad,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d7,
+ 0x1f6dc, 0x1f6ec,
+ 0x1f6f0, 0x1f6fc,
+ 0x1f700, 0x1f776,
+ 0x1f77b, 0x1f7d9,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f7f0, 0x1f7f0,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f8b0, 0x1f8b1,
+ 0x1f900, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa7c,
+ 0x1fa80, 0x1fa88,
+ 0x1fa90, 0x1fabd,
+ 0x1fabf, 0x1fac5,
+ 0x1face, 0x1fadb,
+ 0x1fae0, 0x1fae8,
+ 0x1faf0, 0x1faf8,
+ 0x1fb00, 0x1fb92,
+ 0x1fb94, 0x1fbca,
+ 0x1fbf0, 0x1fbf9,
+ 0x1fffe, 0x2a6df,
+ 0x2a700, 0x2b739,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x3134a,
+ 0x31350, 0x323af,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_15_0 */
+
#endif /* USE_UNICODE_AGE_PROPERTIES */
/* 'Grapheme_Cluster_Break_Prepend': Grapheme_Cluster_Break=Prepend */
static const OnigCodePoint CR_Grapheme_Cluster_Break_Prepend[] = {
- 14,
+ 15,
0x0600, 0x0605,
0x06dd, 0x06dd,
0x070f, 0x070f,
@@ -36650,6 +37652,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Prepend[] = {
0x11a3a, 0x11a3a,
0x11a84, 0x11a89,
0x11d46, 0x11d46,
+ 0x11f02, 0x11f02,
}; /* CR_Grapheme_Cluster_Break_Prepend */
/* 'Grapheme_Cluster_Break_CR': Grapheme_Cluster_Break=CR */
@@ -36677,7 +37680,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Control[] = {
0x2060, 0x206f,
0xfeff, 0xfeff,
0xfff0, 0xfffb,
- 0x13430, 0x13438,
+ 0x13430, 0x1343f,
0x1bca0, 0x1bca3,
0x1d173, 0x1d17a,
0xe0000, 0xe001f,
@@ -36687,7 +37690,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Control[] = {
/* 'Grapheme_Cluster_Break_Extend': Grapheme_Cluster_Break=Extend */
static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
- 354,
+ 364,
0x0300, 0x036f,
0x0483, 0x0489,
0x0591, 0x05bd,
@@ -36790,7 +37793,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x0e47, 0x0e4e,
0x0eb1, 0x0eb1,
0x0eb4, 0x0ebc,
- 0x0ec8, 0x0ecd,
+ 0x0ec8, 0x0ece,
0x0f18, 0x0f19,
0x0f35, 0x0f35,
0x0f37, 0x0f37,
@@ -36919,6 +37922,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x10ae5, 0x10ae6,
0x10d24, 0x10d27,
0x10eab, 0x10eac,
+ 0x10efd, 0x10eff,
0x10f46, 0x10f50,
0x10f82, 0x10f85,
0x11001, 0x11001,
@@ -36941,6 +37945,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x11234, 0x11234,
0x11236, 0x11237,
0x1123e, 0x1123e,
+ 0x11241, 0x11241,
0x112df, 0x112df,
0x112e3, 0x112ea,
0x11300, 0x11301,
@@ -37008,6 +38013,12 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x11d95, 0x11d95,
0x11d97, 0x11d97,
0x11ef3, 0x11ef4,
+ 0x11f00, 0x11f01,
+ 0x11f36, 0x11f3a,
+ 0x11f40, 0x11f40,
+ 0x11f42, 0x11f42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
0x16af0, 0x16af4,
0x16b30, 0x16b36,
0x16f4f, 0x16f4f,
@@ -37034,9 +38045,11 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
0x1e01b, 0x1e021,
0x1e023, 0x1e024,
0x1e026, 0x1e02a,
+ 0x1e08f, 0x1e08f,
0x1e130, 0x1e136,
0x1e2ae, 0x1e2ae,
0x1e2ec, 0x1e2ef,
+ 0x1e4ec, 0x1e4ef,
0x1e8d0, 0x1e8d6,
0x1e944, 0x1e94a,
0x1f3fb, 0x1f3ff,
@@ -37049,7 +38062,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
/* 'Grapheme_Cluster_Break_SpacingMark': Grapheme_Cluster_Break=SpacingMark */
static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
- 161,
+ 165,
0x0903, 0x0903,
0x093b, 0x093b,
0x093e, 0x0940,
@@ -37081,6 +38094,7 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x0cc3, 0x0cc4,
0x0cc7, 0x0cc8,
0x0cca, 0x0ccb,
+ 0x0cf3, 0x0cf3,
0x0d02, 0x0d03,
0x0d3f, 0x0d40,
0x0d46, 0x0d48,
@@ -37183,7 +38197,6 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x116ac, 0x116ac,
0x116ae, 0x116af,
0x116b6, 0x116b6,
- 0x11720, 0x11721,
0x11726, 0x11726,
0x1182c, 0x1182e,
0x11838, 0x11838,
@@ -37207,6 +38220,10 @@ static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
0x11d93, 0x11d94,
0x11d96, 0x11d96,
0x11ef5, 0x11ef6,
+ 0x11f03, 0x11f03,
+ 0x11f34, 0x11f35,
+ 0x11f3e, 0x11f3f,
+ 0x11f41, 0x11f41,
0x16f51, 0x16f87,
0x16ff0, 0x16ff1,
0x1d166, 0x1d166,
@@ -39275,6 +40292,12 @@ static const OnigCodePoint CR_In_Yezidi[] = {
0x10e80, 0x10ebf,
}; /* CR_In_Yezidi */
+/* 'In_Arabic_Extended_C': Block */
+static const OnigCodePoint CR_In_Arabic_Extended_C[] = {
+ 1,
+ 0x10ec0, 0x10eff,
+}; /* CR_In_Arabic_Extended_C */
+
/* 'In_Old_Sogdian': Block */
static const OnigCodePoint CR_In_Old_Sogdian[] = {
1,
@@ -39458,6 +40481,12 @@ static const OnigCodePoint CR_In_Pau_Cin_Hau[] = {
0x11ac0, 0x11aff,
}; /* CR_In_Pau_Cin_Hau */
+/* 'In_Devanagari_Extended_A': Block */
+static const OnigCodePoint CR_In_Devanagari_Extended_A[] = {
+ 1,
+ 0x11b00, 0x11b5f,
+}; /* CR_In_Devanagari_Extended_A */
+
/* 'In_Bhaiksuki': Block */
static const OnigCodePoint CR_In_Bhaiksuki[] = {
1,
@@ -39488,6 +40517,12 @@ static const OnigCodePoint CR_In_Makasar[] = {
0x11ee0, 0x11eff,
}; /* CR_In_Makasar */
+/* 'In_Kawi': Block */
+static const OnigCodePoint CR_In_Kawi[] = {
+ 1,
+ 0x11f00, 0x11f5f,
+}; /* CR_In_Kawi */
+
/* 'In_Lisu_Supplement': Block */
static const OnigCodePoint CR_In_Lisu_Supplement[] = {
1,
@@ -39533,7 +40568,7 @@ static const OnigCodePoint CR_In_Egyptian_Hieroglyphs[] = {
/* 'In_Egyptian_Hieroglyph_Format_Controls': Block */
static const OnigCodePoint CR_In_Egyptian_Hieroglyph_Format_Controls[] = {
1,
- 0x13430, 0x1343f,
+ 0x13430, 0x1345f,
}; /* CR_In_Egyptian_Hieroglyph_Format_Controls */
/* 'In_Anatolian_Hieroglyphs': Block */
@@ -39680,6 +40715,12 @@ static const OnigCodePoint CR_In_Ancient_Greek_Musical_Notation[] = {
0x1d200, 0x1d24f,
}; /* CR_In_Ancient_Greek_Musical_Notation */
+/* 'In_Kaktovik_Numerals': Block */
+static const OnigCodePoint CR_In_Kaktovik_Numerals[] = {
+ 1,
+ 0x1d2c0, 0x1d2df,
+}; /* CR_In_Kaktovik_Numerals */
+
/* 'In_Mayan_Numerals': Block */
static const OnigCodePoint CR_In_Mayan_Numerals[] = {
1,
@@ -39722,6 +40763,12 @@ static const OnigCodePoint CR_In_Glagolitic_Supplement[] = {
0x1e000, 0x1e02f,
}; /* CR_In_Glagolitic_Supplement */
+/* 'In_Cyrillic_Extended_D': Block */
+static const OnigCodePoint CR_In_Cyrillic_Extended_D[] = {
+ 1,
+ 0x1e030, 0x1e08f,
+}; /* CR_In_Cyrillic_Extended_D */
+
/* 'In_Nyiakeng_Puachue_Hmong': Block */
static const OnigCodePoint CR_In_Nyiakeng_Puachue_Hmong[] = {
1,
@@ -39740,6 +40787,12 @@ static const OnigCodePoint CR_In_Wancho[] = {
0x1e2c0, 0x1e2ff,
}; /* CR_In_Wancho */
+/* 'In_Nag_Mundari': Block */
+static const OnigCodePoint CR_In_Nag_Mundari[] = {
+ 1,
+ 0x1e4d0, 0x1e4ff,
+}; /* CR_In_Nag_Mundari */
+
/* 'In_Ethiopic_Extended_B': Block */
static const OnigCodePoint CR_In_Ethiopic_Extended_B[] = {
1,
@@ -39914,6 +40967,12 @@ static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_G[] = {
0x30000, 0x3134f,
}; /* CR_In_CJK_Unified_Ideographs_Extension_G */
+/* 'In_CJK_Unified_Ideographs_Extension_H': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_H[] = {
+ 1,
+ 0x31350, 0x323af,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_H */
+
/* 'In_Tags': Block */
static const OnigCodePoint CR_In_Tags[] = {
1,
@@ -39952,7 +41011,6 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x10bb0, 0x10bff,
0x10c50, 0x10c7f,
0x10d40, 0x10e5f,
- 0x10ec0, 0x10eff,
0x11250, 0x1127f,
0x11380, 0x113ff,
0x114e0, 0x1157f,
@@ -39960,12 +41018,12 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x11750, 0x117ff,
0x11850, 0x1189f,
0x11960, 0x1199f,
- 0x11b00, 0x11bff,
+ 0x11b60, 0x11bff,
0x11cc0, 0x11cff,
0x11db0, 0x11edf,
- 0x11f00, 0x11faf,
+ 0x11f60, 0x11faf,
0x12550, 0x12f8f,
- 0x13440, 0x143ff,
+ 0x13460, 0x143ff,
0x14680, 0x167ff,
0x16b90, 0x16e3f,
0x16ea0, 0x16eff,
@@ -39974,12 +41032,13 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x1b300, 0x1bbff,
0x1bcb0, 0x1ceff,
0x1cfd0, 0x1cfff,
- 0x1d250, 0x1d2df,
+ 0x1d250, 0x1d2bf,
0x1d380, 0x1d3ff,
0x1dab0, 0x1deff,
- 0x1e030, 0x1e0ff,
+ 0x1e090, 0x1e0ff,
0x1e150, 0x1e28f,
- 0x1e300, 0x1e7df,
+ 0x1e300, 0x1e4cf,
+ 0x1e500, 0x1e7df,
0x1e8e0, 0x1e8ff,
0x1e960, 0x1ec6f,
0x1ecc0, 0x1ecff,
@@ -39989,7 +41048,7 @@ static const OnigCodePoint CR_In_No_Block[] = {
0x2a6e0, 0x2a6ff,
0x2ebf0, 0x2f7ff,
0x2fa20, 0x2ffff,
- 0x31350, 0xdffff,
+ 0x323b0, 0xdffff,
0xe0080, 0xe00ff,
0xe01f0, 0xeffff,
}; /* CR_In_No_Block */
@@ -40233,6 +41292,8 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_Tangsa,
CR_Toto,
CR_Vithkuqi,
+ CR_Kawi,
+ CR_Nag_Mundari,
CR_White_Space,
CR_Bidi_Control,
CR_Join_Control,
@@ -40299,6 +41360,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_Age_12_1,
CR_Age_13_0,
CR_Age_14_0,
+ CR_Age_15_0,
#endif /* USE_UNICODE_AGE_PROPERTIES */
CR_Grapheme_Cluster_Break_Prepend,
CR_Grapheme_Cluster_Break_CR,
@@ -40522,6 +41584,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Hanifi_Rohingya,
CR_In_Rumi_Numeral_Symbols,
CR_In_Yezidi,
+ CR_In_Arabic_Extended_C,
CR_In_Old_Sogdian,
CR_In_Sogdian,
CR_In_Old_Uyghur,
@@ -40553,11 +41616,13 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Soyombo,
CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended_A,
CR_In_Pau_Cin_Hau,
+ CR_In_Devanagari_Extended_A,
CR_In_Bhaiksuki,
CR_In_Marchen,
CR_In_Masaram_Gondi,
CR_In_Gunjala_Gondi,
CR_In_Makasar,
+ CR_In_Kawi,
CR_In_Lisu_Supplement,
CR_In_Tamil_Supplement,
CR_In_Cuneiform,
@@ -40590,6 +41655,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Byzantine_Musical_Symbols,
CR_In_Musical_Symbols,
CR_In_Ancient_Greek_Musical_Notation,
+ CR_In_Kaktovik_Numerals,
CR_In_Mayan_Numerals,
CR_In_Tai_Xuan_Jing_Symbols,
CR_In_Counting_Rod_Numerals,
@@ -40597,9 +41663,11 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_Sutton_SignWriting,
CR_In_Latin_Extended_G,
CR_In_Glagolitic_Supplement,
+ CR_In_Cyrillic_Extended_D,
CR_In_Nyiakeng_Puachue_Hmong,
CR_In_Toto,
CR_In_Wancho,
+ CR_In_Nag_Mundari,
CR_In_Ethiopic_Extended_B,
CR_In_Mende_Kikakui,
CR_In_Adlam,
@@ -40629,6 +41697,7 @@ static const OnigCodePoint* const CodeRanges[] = {
CR_In_CJK_Unified_Ideographs_Extension_F,
CR_In_CJK_Compatibility_Ideographs_Supplement,
CR_In_CJK_Unified_Ideographs_Extension_G,
+ CR_In_CJK_Unified_Ideographs_Extension_H,
CR_In_Tags,
CR_In_Variation_Selectors_Supplement,
CR_In_Supplementary_Private_Use_Area_A,
@@ -40642,11 +41711,7 @@ struct uniname2ctype_struct {
};
#define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str)
-static const struct uniname2ctype_struct *uniname2ctype_p(
-#if !(1+0) /* if ANSI, old style not to conflict with generated prototype */
- const char *, unsigned int
-#endif
-);
+static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len);
#ifndef USE_UNICODE_PROPERTIES
#define TOTAL_KEYWORDS 15
@@ -40657,9 +41722,9 @@ static const struct uniname2ctype_struct *uniname2ctype_p(
/* maximum key range = 15, duplicates = 0 */
#else /* USE_UNICODE_PROPERTIES */
#ifndef USE_UNICODE_AGE_PROPERTIES
-#define TOTAL_KEYWORDS 856
+#define TOTAL_KEYWORDS 866
#else /* USE_UNICODE_AGE_PROPERTIES */
-#define TOTAL_KEYWORDS 880
+#define TOTAL_KEYWORDS 891
#endif /* USE_UNICODE_AGE_PROPERTIES */
#define MIN_WORD_LENGTH 1
#define MAX_WORD_LENGTH 45
@@ -40708,13 +41773,13 @@ uniname2ctype_hash (register const char *str, register size_t len)
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
#else /* USE_UNICODE_AGE_PROPERTIES */
6099, 6099, 6099, 6099, 6099, 6099, 12, 6099, 3, 1,
- 4, 8, 36, 24, 14, 16, 10, 7, 6099, 6099,
+ 4, 8, 32, 26, 14, 17, 10, 7, 6099, 6099,
#endif /* USE_UNICODE_AGE_PROPERTIES */
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
6099, 6099, 6099, 6099, 6099, 6099, 6099, 1, 1425, 113,
- 437, 37, 1086, 1071, 1051, 4, 1267, 9, 500, 88,
+ 437, 37, 1086, 1071, 1051, 4, 1984, 9, 500, 88,
8, 18, 1371, 1287, 54, 203, 310, 619, 1958, 603,
275, 1624, 44, 1, 22, 6099, 6099, 6099, 6099, 6099
#endif /* USE_UNICODE_PROPERTIES */
@@ -40944,6 +42009,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str636[sizeof("innewa")];
char uniname2ctype_pool_str639[sizeof("sk")];
char uniname2ctype_pool_str642[sizeof("control")];
+ char uniname2ctype_pool_str643[sizeof("inkawi")];
char uniname2ctype_pool_str645[sizeof("inancientsymbols")];
char uniname2ctype_pool_str647[sizeof("palm")];
char uniname2ctype_pool_str650[sizeof("inlycian")];
@@ -40958,6 +42024,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str695[sizeof("inwarangciti")];
char uniname2ctype_pool_str696[sizeof("sora")];
char uniname2ctype_pool_str697[sizeof("inopticalcharacterrecognition")];
+ char uniname2ctype_pool_str700[sizeof("kawi")];
char uniname2ctype_pool_str703[sizeof("inoldsogdian")];
char uniname2ctype_pool_str705[sizeof("inmalayalam")];
char uniname2ctype_pool_str707[sizeof("bamum")];
@@ -41078,11 +42145,13 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1182[sizeof("inogham")];
char uniname2ctype_pool_str1183[sizeof("cher")];
char uniname2ctype_pool_str1185[sizeof("chakma")];
+ char uniname2ctype_pool_str1186[sizeof("inkaktoviknumerals")];
char uniname2ctype_pool_str1190[sizeof("emoji")];
char uniname2ctype_pool_str1191[sizeof("insiddham")];
char uniname2ctype_pool_str1197[sizeof("cherokee")];
char uniname2ctype_pool_str1198[sizeof("khar")];
char uniname2ctype_pool_str1203[sizeof("inmongolian")];
+ char uniname2ctype_pool_str1204[sizeof("innagmundari")];
char uniname2ctype_pool_str1207[sizeof("incherokeesupplement")];
char uniname2ctype_pool_str1209[sizeof("manichaean")];
char uniname2ctype_pool_str1212[sizeof("inolchiki")];
@@ -41126,48 +42195,46 @@ struct uniname2ctype_pool_t
#ifdef USE_UNICODE_AGE_PROPERTIES
char uniname2ctype_pool_str1257[sizeof("age=6.0")];
char uniname2ctype_pool_str1258[sizeof("age=6.2")];
- char uniname2ctype_pool_str1259[sizeof("age=7.0")];
+ char uniname2ctype_pool_str1259[sizeof("age=15.0")];
+ char uniname2ctype_pool_str1260[sizeof("age=7.0")];
char uniname2ctype_pool_str1262[sizeof("age=6.3")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1263[sizeof("cwt")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1265[sizeof("age=5.1")];
+ char uniname2ctype_pool_str1265[sizeof("age=14.0")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1266[sizeof("unassigned")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1267[sizeof("age=5.0")];
- char uniname2ctype_pool_str1268[sizeof("age=5.2")];
- char uniname2ctype_pool_str1269[sizeof("age=14.0")];
+ char uniname2ctype_pool_str1267[sizeof("age=5.1")];
+ char uniname2ctype_pool_str1269[sizeof("age=5.0")];
+ char uniname2ctype_pool_str1270[sizeof("age=5.2")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1271[sizeof("diacritic")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1273[sizeof("age=4.1")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1274[sizeof("ahom")];
#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str1277[sizeof("age=4.1")];
- char uniname2ctype_pool_str1279[sizeof("age=4.0")];
+ char uniname2ctype_pool_str1275[sizeof("age=4.0")];
#endif /* USE_UNICODE_AGE_PROPERTIES */
char uniname2ctype_pool_str1282[sizeof("incjkunifiedideographsextensione")];
- char uniname2ctype_pool_str1284[sizeof("hani")];
char uniname2ctype_pool_str1285[sizeof("khmr")];
- char uniname2ctype_pool_str1287[sizeof("han")];
char uniname2ctype_pool_str1289[sizeof("insinhala")];
char uniname2ctype_pool_str1292[sizeof("inmiscellaneoustechnical")];
char uniname2ctype_pool_str1297[sizeof("saur")];
- char uniname2ctype_pool_str1298[sizeof("hano")];
char uniname2ctype_pool_str1300[sizeof("guru")];
char uniname2ctype_pool_str1301[sizeof("sundanese")];
char uniname2ctype_pool_str1306[sizeof("punct")];
char uniname2ctype_pool_str1314[sizeof("paucinhau")];
char uniname2ctype_pool_str1317[sizeof("gurmukhi")];
- char uniname2ctype_pool_str1323[sizeof("inkhojki")];
- char uniname2ctype_pool_str1327[sizeof("hanunoo")];
char uniname2ctype_pool_str1328[sizeof("chorasmian")];
- char uniname2ctype_pool_str1330[sizeof("hira")];
char uniname2ctype_pool_str1331[sizeof("logicalorderexception")];
char uniname2ctype_pool_str1340[sizeof("khmer")];
char uniname2ctype_pool_str1343[sizeof("limbu")];
char uniname2ctype_pool_str1349[sizeof("chrs")];
char uniname2ctype_pool_str1352[sizeof("oriya")];
char uniname2ctype_pool_str1354[sizeof("inscriptionalpahlavi")];
+ char uniname2ctype_pool_str1356[sizeof("incyrillicextendedd")];
char uniname2ctype_pool_str1358[sizeof("incjkunifiedideographsextensionc")];
char uniname2ctype_pool_str1360[sizeof("cntrl")];
char uniname2ctype_pool_str1365[sizeof("inlatinextendedadditional")];
@@ -41235,7 +42302,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1587[sizeof("indogra")];
char uniname2ctype_pool_str1597[sizeof("arab")];
char uniname2ctype_pool_str1598[sizeof("medefaidrin")];
- char uniname2ctype_pool_str1601[sizeof("hatran")];
char uniname2ctype_pool_str1607[sizeof("inshorthandformatcontrols")];
char uniname2ctype_pool_str1613[sizeof("phli")];
char uniname2ctype_pool_str1617[sizeof("inimperialaramaic")];
@@ -41244,7 +42310,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1623[sizeof("inanatolianhieroglyphs")];
char uniname2ctype_pool_str1629[sizeof("punctuation")];
char uniname2ctype_pool_str1635[sizeof("graphemeextend")];
- char uniname2ctype_pool_str1636[sizeof("hatr")];
char uniname2ctype_pool_str1643[sizeof("cwl")];
char uniname2ctype_pool_str1644[sizeof("vith")];
char uniname2ctype_pool_str1654[sizeof("ingeometricshapes")];
@@ -41302,7 +42367,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1845[sizeof("oidc")];
char uniname2ctype_pool_str1848[sizeof("bopo")];
char uniname2ctype_pool_str1851[sizeof("cuneiform")];
- char uniname2ctype_pool_str1857[sizeof("hex")];
char uniname2ctype_pool_str1866[sizeof("caseignorable")];
char uniname2ctype_pool_str1871[sizeof("inoldpersian")];
char uniname2ctype_pool_str1881[sizeof("cwu")];
@@ -41321,10 +42385,8 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1935[sizeof("oids")];
char uniname2ctype_pool_str1936[sizeof("inarabicextendeda")];
char uniname2ctype_pool_str1941[sizeof("modifierletter")];
- char uniname2ctype_pool_str1948[sizeof("gujr")];
char uniname2ctype_pool_str1950[sizeof("incjksymbolsandpunctuation")];
char uniname2ctype_pool_str1956[sizeof("olower")];
- char uniname2ctype_pool_str1957[sizeof("gujarati")];
char uniname2ctype_pool_str1958[sizeof("bopomofo")];
char uniname2ctype_pool_str1964[sizeof("inlisu")];
char uniname2ctype_pool_str1967[sizeof("inoldpermic")];
@@ -41338,19 +42400,26 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str1993[sizeof("inbalinese")];
char uniname2ctype_pool_str1994[sizeof("sorasompeng")];
char uniname2ctype_pool_str1996[sizeof("closepunctuation")];
+ char uniname2ctype_pool_str2001[sizeof("hani")];
char uniname2ctype_pool_str2002[sizeof("inmayannumerals")];
+ char uniname2ctype_pool_str2004[sizeof("han")];
char uniname2ctype_pool_str2006[sizeof("inmiscellaneousmathematicalsymbolsb")];
char uniname2ctype_pool_str2010[sizeof("inlepcha")];
char uniname2ctype_pool_str2011[sizeof("patsyn")];
char uniname2ctype_pool_str2012[sizeof("inlisusupplement")];
char uniname2ctype_pool_str2014[sizeof("insyriacsupplement")];
+ char uniname2ctype_pool_str2015[sizeof("hano")];
char uniname2ctype_pool_str2016[sizeof("newa")];
char uniname2ctype_pool_str2023[sizeof("spacingmark")];
char uniname2ctype_pool_str2024[sizeof("inpalmyrene")];
char uniname2ctype_pool_str2026[sizeof("takr")];
char uniname2ctype_pool_str2033[sizeof("xposixpunct")];
+ char uniname2ctype_pool_str2040[sizeof("inkhojki")];
char uniname2ctype_pool_str2042[sizeof("taile")];
char uniname2ctype_pool_str2043[sizeof("assigned")];
+ char uniname2ctype_pool_str2044[sizeof("hanunoo")];
+ char uniname2ctype_pool_str2047[sizeof("hira")];
+ char uniname2ctype_pool_str2048[sizeof("inarabicextendedc")];
char uniname2ctype_pool_str2062[sizeof("newtailue")];
char uniname2ctype_pool_str2070[sizeof("space")];
char uniname2ctype_pool_str2073[sizeof("intelugu")];
@@ -41401,41 +42470,35 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2278[sizeof("shaw")];
char uniname2ctype_pool_str2279[sizeof("palmyrene")];
char uniname2ctype_pool_str2283[sizeof("soyo")];
+ char uniname2ctype_pool_str2296[sizeof("incjkunifiedideographsextensionh")];
char uniname2ctype_pool_str2305[sizeof("sgnw")];
char uniname2ctype_pool_str2308[sizeof("toto")];
char uniname2ctype_pool_str2312[sizeof("caucasianalbanian")];
char uniname2ctype_pool_str2315[sizeof("inmathematicalalphanumericsymbols")];
char uniname2ctype_pool_str2316[sizeof("incjkunifiedideographsextensiong")];
+ char uniname2ctype_pool_str2318[sizeof("hatran")];
char uniname2ctype_pool_str2321[sizeof("taiviet")];
char uniname2ctype_pool_str2323[sizeof("meroitichieroglyphs")];
char uniname2ctype_pool_str2327[sizeof("ingeorgianextended")];
char uniname2ctype_pool_str2331[sizeof("incjkunifiedideographsextensionf")];
char uniname2ctype_pool_str2333[sizeof("oldpersian")];
- char uniname2ctype_pool_str2341[sizeof("mahj")];
char uniname2ctype_pool_str2343[sizeof("induployan")];
char uniname2ctype_pool_str2344[sizeof("incyrillicextendedb")];
char uniname2ctype_pool_str2345[sizeof("dash")];
- char uniname2ctype_pool_str2350[sizeof("mahajani")];
- char uniname2ctype_pool_str2351[sizeof("hang")];
+ char uniname2ctype_pool_str2353[sizeof("hatr")];
char uniname2ctype_pool_str2361[sizeof("innyiakengpuachuehmong")];
char uniname2ctype_pool_str2364[sizeof("incombiningdiacriticalmarks")];
- char uniname2ctype_pool_str2370[sizeof("ingujarati")];
char uniname2ctype_pool_str2373[sizeof("nl")];
char uniname2ctype_pool_str2374[sizeof("incombiningdiacriticalmarksforsymbols")];
char uniname2ctype_pool_str2375[sizeof("khudawadi")];
- char uniname2ctype_pool_str2389[sizeof("ingunjalagondi")];
char uniname2ctype_pool_str2397[sizeof("incjkradicalssupplement")];
char uniname2ctype_pool_str2398[sizeof("inglagolitic")];
char uniname2ctype_pool_str2405[sizeof("orkh")];
- char uniname2ctype_pool_str2406[sizeof("hiragana")];
char uniname2ctype_pool_str2414[sizeof("syrc")];
- char uniname2ctype_pool_str2418[sizeof("inrejang")];
char uniname2ctype_pool_str2427[sizeof("surrogate")];
- char uniname2ctype_pool_str2428[sizeof("khoj")];
char uniname2ctype_pool_str2433[sizeof("indevanagari")];
char uniname2ctype_pool_str2434[sizeof("avestan")];
char uniname2ctype_pool_str2437[sizeof("oldpermic")];
- char uniname2ctype_pool_str2438[sizeof("hmng")];
char uniname2ctype_pool_str2440[sizeof("ethi")];
char uniname2ctype_pool_str2451[sizeof("ogam")];
char uniname2ctype_pool_str2454[sizeof("rohg")];
@@ -41443,7 +42506,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2464[sizeof("java")];
char uniname2ctype_pool_str2470[sizeof("inphagspa")];
char uniname2ctype_pool_str2475[sizeof("lepcha")];
- char uniname2ctype_pool_str2476[sizeof("inenclosedcjklettersandmonths")];
+ char uniname2ctype_pool_str2476[sizeof("indevanagariextendeda")];
char uniname2ctype_pool_str2478[sizeof("intifinagh")];
char uniname2ctype_pool_str2479[sizeof("intagalog")];
char uniname2ctype_pool_str2481[sizeof("incombiningdiacriticalmarkssupplement")];
@@ -41453,6 +42516,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2513[sizeof("insymbolsandpictographsextendeda")];
char uniname2ctype_pool_str2530[sizeof("syriac")];
char uniname2ctype_pool_str2534[sizeof("inbengali")];
+ char uniname2ctype_pool_str2535[sizeof("nagm")];
char uniname2ctype_pool_str2545[sizeof("extendedpictographic")];
char uniname2ctype_pool_str2548[sizeof("buhd")];
char uniname2ctype_pool_str2549[sizeof("javanese")];
@@ -41461,6 +42525,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2567[sizeof("inlatin1supplement")];
char uniname2ctype_pool_str2570[sizeof("ingothic")];
char uniname2ctype_pool_str2572[sizeof("invariationselectors")];
+ char uniname2ctype_pool_str2574[sizeof("hex")];
char uniname2ctype_pool_str2575[sizeof("inverticalforms")];
char uniname2ctype_pool_str2576[sizeof("ebase")];
char uniname2ctype_pool_str2582[sizeof("incurrencysymbols")];
@@ -41474,15 +42539,15 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2652[sizeof("invedicextensions")];
char uniname2ctype_pool_str2656[sizeof("inlimbu")];
char uniname2ctype_pool_str2657[sizeof("olditalic")];
- char uniname2ctype_pool_str2660[sizeof("rjng")];
+ char uniname2ctype_pool_str2665[sizeof("gujr")];
char uniname2ctype_pool_str2666[sizeof("mathsymbol")];
char uniname2ctype_pool_str2670[sizeof("incjkunifiedideographsextensionb")];
+ char uniname2ctype_pool_str2674[sizeof("gujarati")];
char uniname2ctype_pool_str2688[sizeof("phagspa")];
char uniname2ctype_pool_str2689[sizeof("invariationselectorssupplement")];
char uniname2ctype_pool_str2694[sizeof("currencysymbol")];
char uniname2ctype_pool_str2705[sizeof("inlinearbsyllabary")];
char uniname2ctype_pool_str2726[sizeof("wancho")];
- char uniname2ctype_pool_str2738[sizeof("hmnp")];
char uniname2ctype_pool_str2750[sizeof("inpaucinhau")];
char uniname2ctype_pool_str2761[sizeof("other")];
char uniname2ctype_pool_str2762[sizeof("otheridcontinue")];
@@ -41492,7 +42557,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2772[sizeof("warangciti")];
char uniname2ctype_pool_str2775[sizeof("othernumber")];
char uniname2ctype_pool_str2786[sizeof("digit")];
- char uniname2ctype_pool_str2787[sizeof("hebr")];
char uniname2ctype_pool_str2793[sizeof("nonspacingmark")];
char uniname2ctype_pool_str2801[sizeof("titlecaseletter")];
char uniname2ctype_pool_str2808[sizeof("inmeroiticcursive")];
@@ -41512,7 +42576,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2871[sizeof("noncharactercodepoint")];
char uniname2ctype_pool_str2879[sizeof("oldhungarian")];
char uniname2ctype_pool_str2886[sizeof("insymbolsforlegacycomputing")];
- char uniname2ctype_pool_str2901[sizeof("hangul")];
char uniname2ctype_pool_str2902[sizeof("insmallformvariants")];
char uniname2ctype_pool_str2904[sizeof("inhangulsyllables")];
char uniname2ctype_pool_str2905[sizeof("emojipresentation")];
@@ -41524,15 +42587,12 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str2964[sizeof("inpsalterpahlavi")];
char uniname2ctype_pool_str2966[sizeof("whitespace")];
char uniname2ctype_pool_str2967[sizeof("finalpunctuation")];
- char uniname2ctype_pool_str2969[sizeof("hung")];
char uniname2ctype_pool_str2970[sizeof("orya")];
- char uniname2ctype_pool_str2972[sizeof("hexdigit")];
char uniname2ctype_pool_str2980[sizeof("phlp")];
char uniname2ctype_pool_str2984[sizeof("inbamumsupplement")];
char uniname2ctype_pool_str2986[sizeof("buhid")];
char uniname2ctype_pool_str2987[sizeof("paragraphseparator")];
char uniname2ctype_pool_str2988[sizeof("inalphabeticpresentationforms")];
- char uniname2ctype_pool_str2993[sizeof("hluw")];
char uniname2ctype_pool_str2997[sizeof("inlatinextendedg")];
char uniname2ctype_pool_str3001[sizeof("elba")];
char uniname2ctype_pool_str3002[sizeof("changeswhentitlecased")];
@@ -41549,30 +42609,39 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3048[sizeof("tagbanwa")];
char uniname2ctype_pool_str3052[sizeof("tamil")];
char uniname2ctype_pool_str3053[sizeof("khitansmallscript")];
+ char uniname2ctype_pool_str3058[sizeof("mahj")];
+ char uniname2ctype_pool_str3067[sizeof("mahajani")];
+ char uniname2ctype_pool_str3068[sizeof("hang")];
char uniname2ctype_pool_str3071[sizeof("tirh")];
char uniname2ctype_pool_str3072[sizeof("sylotinagri")];
char uniname2ctype_pool_str3082[sizeof("talu")];
+ char uniname2ctype_pool_str3084[sizeof("nagmundari")];
char uniname2ctype_pool_str3086[sizeof("deva")];
+ char uniname2ctype_pool_str3087[sizeof("ingujarati")];
char uniname2ctype_pool_str3091[sizeof("deprecated")];
char uniname2ctype_pool_str3099[sizeof("inarabicpresentationformsb")];
char uniname2ctype_pool_str3104[sizeof("devanagari")];
+ char uniname2ctype_pool_str3106[sizeof("ingunjalagondi")];
char uniname2ctype_pool_str3107[sizeof("graphemeclusterbreak=t")];
char uniname2ctype_pool_str3109[sizeof("graphemeclusterbreak=lvt")];
char uniname2ctype_pool_str3110[sizeof("taitham")];
char uniname2ctype_pool_str3111[sizeof("nbat")];
char uniname2ctype_pool_str3118[sizeof("telu")];
+ char uniname2ctype_pool_str3123[sizeof("hiragana")];
char uniname2ctype_pool_str3125[sizeof("nabataean")];
- char uniname2ctype_pool_str3140[sizeof("inmahjongtiles")];
+ char uniname2ctype_pool_str3135[sizeof("inrejang")];
char uniname2ctype_pool_str3142[sizeof("intangutsupplement")];
+ char uniname2ctype_pool_str3145[sizeof("khoj")];
+ char uniname2ctype_pool_str3155[sizeof("hmng")];
char uniname2ctype_pool_str3157[sizeof("cyprominoan")];
char uniname2ctype_pool_str3158[sizeof("inhebrew")];
char uniname2ctype_pool_str3176[sizeof("inmathematicaloperators")];
char uniname2ctype_pool_str3180[sizeof("inarabicsupplement")];
+ char uniname2ctype_pool_str3193[sizeof("inenclosedcjklettersandmonths")];
char uniname2ctype_pool_str3209[sizeof("changeswhenlowercased")];
char uniname2ctype_pool_str3212[sizeof("tangut")];
char uniname2ctype_pool_str3215[sizeof("elbasan")];
char uniname2ctype_pool_str3218[sizeof("osmanya")];
- char uniname2ctype_pool_str3227[sizeof("inyijinghexagramsymbols")];
char uniname2ctype_pool_str3237[sizeof("insuperscriptsandsubscripts")];
char uniname2ctype_pool_str3239[sizeof("graphemeclusterbreak=extend")];
char uniname2ctype_pool_str3240[sizeof("graphemeclusterbreak=prepend")];
@@ -41583,7 +42652,6 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3275[sizeof("kayahli")];
char uniname2ctype_pool_str3284[sizeof("inplayingcards")];
char uniname2ctype_pool_str3287[sizeof("elym")];
- char uniname2ctype_pool_str3290[sizeof("injavanese")];
char uniname2ctype_pool_str3297[sizeof("graphemeclusterbreak=l")];
char uniname2ctype_pool_str3303[sizeof("graphemeclusterbreak=control")];
char uniname2ctype_pool_str3313[sizeof("ogrext")];
@@ -41599,6 +42667,7 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3371[sizeof("cypriot")];
char uniname2ctype_pool_str3372[sizeof("any")];
char uniname2ctype_pool_str3373[sizeof("otheruppercase")];
+ char uniname2ctype_pool_str3377[sizeof("rjng")];
char uniname2ctype_pool_str3391[sizeof("wspace")];
char uniname2ctype_pool_str3396[sizeof("inindicsiyaqnumbers")];
char uniname2ctype_pool_str3405[sizeof("inprivateusearea")];
@@ -41606,11 +42675,12 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3428[sizeof("oupper")];
char uniname2ctype_pool_str3433[sizeof("signwriting")];
char uniname2ctype_pool_str3436[sizeof("nushu")];
- char uniname2ctype_pool_str3452[sizeof("hanifirohingya")];
+ char uniname2ctype_pool_str3455[sizeof("hmnp")];
char uniname2ctype_pool_str3458[sizeof("upper")];
char uniname2ctype_pool_str3460[sizeof("insupplementalarrowsc")];
char uniname2ctype_pool_str3483[sizeof("omath")];
char uniname2ctype_pool_str3502[sizeof("modifiersymbol")];
+ char uniname2ctype_pool_str3504[sizeof("hebr")];
char uniname2ctype_pool_str3505[sizeof("inhalfwidthandfullwidthforms")];
char uniname2ctype_pool_str3511[sizeof("insupplementalmathematicaloperators")];
char uniname2ctype_pool_str3532[sizeof("inpahawhhmong")];
@@ -41619,60 +42689,68 @@ struct uniname2ctype_pool_t
char uniname2ctype_pool_str3580[sizeof("dupl")];
char uniname2ctype_pool_str3590[sizeof("ogham")];
char uniname2ctype_pool_str3613[sizeof("dashpunctuation")];
+ char uniname2ctype_pool_str3618[sizeof("hangul")];
char uniname2ctype_pool_str3648[sizeof("inhanguljamoextendedb")];
char uniname2ctype_pool_str3659[sizeof("bassavah")];
char uniname2ctype_pool_str3664[sizeof("aghb")];
+ char uniname2ctype_pool_str3686[sizeof("hung")];
+ char uniname2ctype_pool_str3689[sizeof("hexdigit")];
char uniname2ctype_pool_str3698[sizeof("incypriotsyllabary")];
char uniname2ctype_pool_str3699[sizeof("indivesakuru")];
char uniname2ctype_pool_str3701[sizeof("tibt")];
char uniname2ctype_pool_str3705[sizeof("inlatinextendedb")];
+ char uniname2ctype_pool_str3710[sizeof("hluw")];
char uniname2ctype_pool_str3713[sizeof("tibetan")];
char uniname2ctype_pool_str3721[sizeof("inyisyllables")];
char uniname2ctype_pool_str3744[sizeof("oldnortharabian")];
char uniname2ctype_pool_str3754[sizeof("defaultignorablecodepoint")];
char uniname2ctype_pool_str3766[sizeof("inhighprivateusesurrogates")];
- char uniname2ctype_pool_str3770[sizeof("rejang")];
char uniname2ctype_pool_str3799[sizeof("soyombo")];
char uniname2ctype_pool_str3807[sizeof("otherdefaultignorablecodepoint")];
char uniname2ctype_pool_str3842[sizeof("pahawhhmong")];
char uniname2ctype_pool_str3845[sizeof("unifiedideograph")];
char uniname2ctype_pool_str3850[sizeof("othermath")];
char uniname2ctype_pool_str3854[sizeof("changeswhencasefolded")];
+ char uniname2ctype_pool_str3857[sizeof("inmahjongtiles")];
char uniname2ctype_pool_str3868[sizeof("dep")];
char uniname2ctype_pool_str3881[sizeof("divesakuru")];
char uniname2ctype_pool_str3884[sizeof("graphemeclusterbreak=lf")];
char uniname2ctype_pool_str3891[sizeof("uppercaseletter")];
char uniname2ctype_pool_str3924[sizeof("insupplementalpunctuation")];
char uniname2ctype_pool_str3942[sizeof("ethiopic")];
+ char uniname2ctype_pool_str3944[sizeof("inyijinghexagramsymbols")];
char uniname2ctype_pool_str3949[sizeof("ecomp")];
char uniname2ctype_pool_str3976[sizeof("inglagoliticsupplement")];
- char uniname2ctype_pool_str3978[sizeof("hebrew")];
char uniname2ctype_pool_str3998[sizeof("inbopomofoextended")];
- char uniname2ctype_pool_str4066[sizeof("graphemeclusterbreak=zwj")];
+ char uniname2ctype_pool_str4007[sizeof("injavanese")];
char uniname2ctype_pool_str4106[sizeof("otherpunctuation")];
char uniname2ctype_pool_str4116[sizeof("tifinagh")];
char uniname2ctype_pool_str4127[sizeof("tfng")];
+ char uniname2ctype_pool_str4169[sizeof("hanifirohingya")];
char uniname2ctype_pool_str4231[sizeof("tavt")];
char uniname2ctype_pool_str4308[sizeof("inboxdrawing")];
char uniname2ctype_pool_str4309[sizeof("oldsoutharabian")];
- char uniname2ctype_pool_str4321[sizeof("hyphen")];
char uniname2ctype_pool_str4348[sizeof("inegyptianhieroglyphs")];
char uniname2ctype_pool_str4361[sizeof("inegyptianhieroglyphformatcontrols")];
char uniname2ctype_pool_str4459[sizeof("tagb")];
+ char uniname2ctype_pool_str4487[sizeof("rejang")];
char uniname2ctype_pool_str4604[sizeof("tglg")];
char uniname2ctype_pool_str4626[sizeof("tagalog")];
char uniname2ctype_pool_str4627[sizeof("othergraphemeextend")];
char uniname2ctype_pool_str4674[sizeof("insupplementaryprivateuseareaa")];
char uniname2ctype_pool_str4683[sizeof("inhighsurrogates")];
+ char uniname2ctype_pool_str4695[sizeof("hebrew")];
char uniname2ctype_pool_str4734[sizeof("duployan")];
char uniname2ctype_pool_str4755[sizeof("graphemeclusterbreak=v")];
char uniname2ctype_pool_str4756[sizeof("graphemeclusterbreak=lv")];
char uniname2ctype_pool_str4772[sizeof("insupplementalarrowsb")];
+ char uniname2ctype_pool_str4783[sizeof("graphemeclusterbreak=zwj")];
char uniname2ctype_pool_str4810[sizeof("telugu")];
char uniname2ctype_pool_str4898[sizeof("zyyy")];
char uniname2ctype_pool_str4982[sizeof("olduyghur")];
char uniname2ctype_pool_str4986[sizeof("inhangulcompatibilityjamo")];
char uniname2ctype_pool_str5018[sizeof("openpunctuation")];
+ char uniname2ctype_pool_str5038[sizeof("hyphen")];
char uniname2ctype_pool_str5134[sizeof("insupplementalsymbolsandpictographs")];
char uniname2ctype_pool_str5141[sizeof("egyp")];
char uniname2ctype_pool_str5300[sizeof("nyiakengpuachuehmong")];
@@ -41849,6 +42927,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"innewa",
"sk",
"control",
+ "inkawi",
"inancientsymbols",
"palm",
"inlycian",
@@ -41863,6 +42942,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inwarangciti",
"sora",
"inopticalcharacterrecognition",
+ "kawi",
"inoldsogdian",
"inmalayalam",
"bamum",
@@ -41995,11 +43075,13 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inogham",
"cher",
"chakma",
+ "inkaktoviknumerals",
"emoji",
"insiddham",
"cherokee",
"khar",
"inmongolian",
+ "innagmundari",
"incherokeesupplement",
"manichaean",
"inolchiki",
@@ -42043,48 +43125,46 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifdef USE_UNICODE_AGE_PROPERTIES
"age=6.0",
"age=6.2",
+ "age=15.0",
"age=7.0",
"age=6.3",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"cwt",
#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=5.1",
+ "age=14.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"unassigned",
#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=5.1",
"age=5.0",
"age=5.2",
- "age=14.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"diacritic",
- "ahom",
#ifdef USE_UNICODE_AGE_PROPERTIES
"age=4.1",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "ahom",
+#ifdef USE_UNICODE_AGE_PROPERTIES
"age=4.0",
#endif /* USE_UNICODE_AGE_PROPERTIES */
"incjkunifiedideographsextensione",
- "hani",
"khmr",
- "han",
"insinhala",
"inmiscellaneoustechnical",
"saur",
- "hano",
"guru",
"sundanese",
"punct",
"paucinhau",
"gurmukhi",
- "inkhojki",
- "hanunoo",
"chorasmian",
- "hira",
"logicalorderexception",
"khmer",
"limbu",
"chrs",
"oriya",
"inscriptionalpahlavi",
+ "incyrillicextendedd",
"incjkunifiedideographsextensionc",
#endif /* USE_UNICODE_PROPERTIES */
"cntrl",
@@ -42156,7 +43236,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"indogra",
"arab",
"medefaidrin",
- "hatran",
"inshorthandformatcontrols",
"phli",
"inimperialaramaic",
@@ -42165,7 +43244,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inanatolianhieroglyphs",
"punctuation",
"graphemeextend",
- "hatr",
"cwl",
"vith",
"ingeometricshapes",
@@ -42223,7 +43301,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oidc",
"bopo",
"cuneiform",
- "hex",
"caseignorable",
"inoldpersian",
"cwu",
@@ -42242,10 +43319,8 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oids",
"inarabicextendeda",
"modifierletter",
- "gujr",
"incjksymbolsandpunctuation",
"olower",
- "gujarati",
"bopomofo",
"inlisu",
"inoldpermic",
@@ -42259,12 +43334,15 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inbalinese",
"sorasompeng",
"closepunctuation",
+ "hani",
"inmayannumerals",
+ "han",
"inmiscellaneousmathematicalsymbolsb",
"inlepcha",
"patsyn",
"inlisusupplement",
"insyriacsupplement",
+ "hano",
"newa",
"spacingmark",
"inpalmyrene",
@@ -42274,8 +43352,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifndef USE_UNICODE_PROPERTIES
"lower",
#else /* USE_UNICODE_PROPERTIES */
+ "inkhojki",
"taile",
"assigned",
+ "hanunoo",
+ "hira",
+ "inarabicextendedc",
"newtailue",
"space",
"intelugu",
@@ -42328,41 +43410,35 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"shaw",
"palmyrene",
"soyo",
+ "incjkunifiedideographsextensionh",
"sgnw",
"toto",
"caucasianalbanian",
"inmathematicalalphanumericsymbols",
"incjkunifiedideographsextensiong",
+ "hatran",
"taiviet",
"meroitichieroglyphs",
"ingeorgianextended",
"incjkunifiedideographsextensionf",
"oldpersian",
- "mahj",
"induployan",
"incyrillicextendedb",
"dash",
- "mahajani",
- "hang",
+ "hatr",
"innyiakengpuachuehmong",
"incombiningdiacriticalmarks",
- "ingujarati",
"nl",
"incombiningdiacriticalmarksforsymbols",
"khudawadi",
- "ingunjalagondi",
"incjkradicalssupplement",
"inglagolitic",
"orkh",
- "hiragana",
"syrc",
- "inrejang",
"surrogate",
- "khoj",
"indevanagari",
"avestan",
"oldpermic",
- "hmng",
"ethi",
"ogam",
"rohg",
@@ -42370,7 +43446,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"java",
"inphagspa",
"lepcha",
- "inenclosedcjklettersandmonths",
+ "indevanagariextendeda",
"intifinagh",
"intagalog",
"incombiningdiacriticalmarkssupplement",
@@ -42380,6 +43456,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"insymbolsandpictographsextendeda",
"syriac",
"inbengali",
+ "nagm",
"extendedpictographic",
"buhd",
"javanese",
@@ -42388,6 +43465,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inlatin1supplement",
"ingothic",
"invariationselectors",
+ "hex",
"inverticalforms",
"ebase",
"incurrencysymbols",
@@ -42401,15 +43479,15 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"invedicextensions",
"inlimbu",
"olditalic",
- "rjng",
+ "gujr",
"mathsymbol",
"incjkunifiedideographsextensionb",
+ "gujarati",
"phagspa",
"invariationselectorssupplement",
"currencysymbol",
"inlinearbsyllabary",
"wancho",
- "hmnp",
"inpaucinhau",
"other",
"otheridcontinue",
@@ -42423,7 +43501,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
#ifndef USE_UNICODE_PROPERTIES
"blank"
#else /* USE_UNICODE_PROPERTIES */
- "hebr",
"nonspacingmark",
"titlecaseletter",
"inmeroiticcursive",
@@ -42443,7 +43520,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"noncharactercodepoint",
"oldhungarian",
"insymbolsforlegacycomputing",
- "hangul",
"insmallformvariants",
"inhangulsyllables",
"emojipresentation",
@@ -42455,15 +43531,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"inpsalterpahlavi",
"whitespace",
"finalpunctuation",
- "hung",
"orya",
- "hexdigit",
"phlp",
"inbamumsupplement",
"buhid",
"paragraphseparator",
"inalphabeticpresentationforms",
- "hluw",
"inlatinextendedg",
"elba",
"changeswhentitlecased",
@@ -42480,30 +43553,39 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"tagbanwa",
"tamil",
"khitansmallscript",
+ "mahj",
+ "mahajani",
+ "hang",
"tirh",
"sylotinagri",
"talu",
+ "nagmundari",
"deva",
+ "ingujarati",
"deprecated",
"inarabicpresentationformsb",
"devanagari",
+ "ingunjalagondi",
"graphemeclusterbreak=t",
"graphemeclusterbreak=lvt",
"taitham",
"nbat",
"telu",
+ "hiragana",
"nabataean",
- "inmahjongtiles",
+ "inrejang",
"intangutsupplement",
+ "khoj",
+ "hmng",
"cyprominoan",
"inhebrew",
"inmathematicaloperators",
"inarabicsupplement",
+ "inenclosedcjklettersandmonths",
"changeswhenlowercased",
"tangut",
"elbasan",
"osmanya",
- "inyijinghexagramsymbols",
"insuperscriptsandsubscripts",
"graphemeclusterbreak=extend",
"graphemeclusterbreak=prepend",
@@ -42514,7 +43596,6 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"kayahli",
"inplayingcards",
"elym",
- "injavanese",
"graphemeclusterbreak=l",
"graphemeclusterbreak=control",
"ogrext",
@@ -42530,6 +43611,7 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"cypriot",
"any",
"otheruppercase",
+ "rjng",
"wspace",
"inindicsiyaqnumbers",
"inprivateusearea",
@@ -42537,11 +43619,12 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"oupper",
"signwriting",
"nushu",
- "hanifirohingya",
+ "hmnp",
"upper",
"insupplementalarrowsc",
"omath",
"modifiersymbol",
+ "hebr",
"inhalfwidthandfullwidthforms",
"insupplementalmathematicaloperators",
"inpahawhhmong",
@@ -42550,60 +43633,68 @@ static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
"dupl",
"ogham",
"dashpunctuation",
+ "hangul",
"inhanguljamoextendedb",
"bassavah",
"aghb",
+ "hung",
+ "hexdigit",
"incypriotsyllabary",
"indivesakuru",
"tibt",
"inlatinextendedb",
+ "hluw",
"tibetan",
"inyisyllables",
"oldnortharabian",
"defaultignorablecodepoint",
"inhighprivateusesurrogates",
- "rejang",
"soyombo",
"otherdefaultignorablecodepoint",
"pahawhhmong",
"unifiedideograph",
"othermath",
"changeswhencasefolded",
+ "inmahjongtiles",
"dep",
"divesakuru",
"graphemeclusterbreak=lf",
"uppercaseletter",
"insupplementalpunctuation",
"ethiopic",
+ "inyijinghexagramsymbols",
"ecomp",
"inglagoliticsupplement",
- "hebrew",
"inbopomofoextended",
- "graphemeclusterbreak=zwj",
+ "injavanese",
"otherpunctuation",
"tifinagh",
"tfng",
+ "hanifirohingya",
"tavt",
"inboxdrawing",
"oldsoutharabian",
- "hyphen",
"inegyptianhieroglyphs",
"inegyptianhieroglyphformatcontrols",
"tagb",
+ "rejang",
"tglg",
"tagalog",
"othergraphemeextend",
"insupplementaryprivateuseareaa",
"inhighsurrogates",
+ "hebrew",
"duployan",
"graphemeclusterbreak=v",
"graphemeclusterbreak=lv",
"insupplementalarrowsb",
+ "graphemeclusterbreak=zwj",
"telugu",
"zyyy",
"olduyghur",
"inhangulcompatibilityjamo",
"openpunctuation",
+ "hyphen",
"insupplementalsymbolsandpictographs",
"egyp",
"nyiakengpuachuehmong",
@@ -42639,13 +43730,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str48), 95},
{uniname2ctype_offset(str49), 95},
{-1}, {-1},
- {uniname2ctype_offset(str52), 343},
+ {uniname2ctype_offset(str52), 346},
{-1}, {-1},
{uniname2ctype_offset(str55), 21},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str64), 44},
{-1},
- {uniname2ctype_offset(str66), 330},
+ {uniname2ctype_offset(str66), 333},
{uniname2ctype_offset(str67), 52},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str71), 181},
@@ -42663,9 +43754,9 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str94), 33},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str100), 149},
- {uniname2ctype_offset(str101), 510},
+ {uniname2ctype_offset(str101), 513},
{uniname2ctype_offset(str102), 108},
- {uniname2ctype_offset(str103), 261},
+ {uniname2ctype_offset(str103), 263},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str107), 31},
{uniname2ctype_offset(str108), 77},
@@ -42684,27 +43775,27 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str130), 42},
{uniname2ctype_offset(str131), 172},
{-1}, {-1},
- {uniname2ctype_offset(str134), 494},
+ {uniname2ctype_offset(str134), 497},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str139), 170},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str145), 513},
- {uniname2ctype_offset(str146), 569},
+ {uniname2ctype_offset(str145), 516},
+ {uniname2ctype_offset(str146), 575},
{-1},
- {uniname2ctype_offset(str148), 574},
- {uniname2ctype_offset(str149), 531},
+ {uniname2ctype_offset(str148), 580},
+ {uniname2ctype_offset(str149), 535},
{-1},
{uniname2ctype_offset(str151), 18},
{uniname2ctype_offset(str152), 169},
{uniname2ctype_offset(str153), 160},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str158), 276},
+ {uniname2ctype_offset(str158), 278},
{-1}, {-1},
- {uniname2ctype_offset(str161), 324},
+ {uniname2ctype_offset(str161), 327},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str177), 349},
- {uniname2ctype_offset(str178), 558},
+ {uniname2ctype_offset(str177), 352},
+ {uniname2ctype_offset(str178), 563},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str183), 75},
{-1}, {-1},
@@ -42713,19 +43804,19 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str190), 208},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str203), 357},
- {uniname2ctype_offset(str204), 485},
+ {uniname2ctype_offset(str203), 360},
+ {uniname2ctype_offset(str204), 488},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str210), 575},
+ {uniname2ctype_offset(str210), 581},
{-1},
- {uniname2ctype_offset(str212), 362},
+ {uniname2ctype_offset(str212), 365},
{uniname2ctype_offset(str213), 115},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str218), 545},
+ {uniname2ctype_offset(str218), 549},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str226), 171},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str230), 526},
+ {uniname2ctype_offset(str230), 530},
{uniname2ctype_offset(str231), 31},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str236), 25},
@@ -42737,64 +43828,64 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str253), 102},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str260), 562},
+ {uniname2ctype_offset(str260), 568},
{-1}, {-1},
{uniname2ctype_offset(str263), 161},
{-1},
{uniname2ctype_offset(str265), 19},
{-1},
{uniname2ctype_offset(str267), 79},
- {uniname2ctype_offset(str268), 354},
+ {uniname2ctype_offset(str268), 357},
{-1},
- {uniname2ctype_offset(str270), 268},
+ {uniname2ctype_offset(str270), 270},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str274), 561},
- {uniname2ctype_offset(str275), 514},
+ {uniname2ctype_offset(str274), 567},
+ {uniname2ctype_offset(str275), 517},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str281), 318},
+ {uniname2ctype_offset(str281), 321},
{uniname2ctype_offset(str282), 40},
{uniname2ctype_offset(str283), 79},
{-1},
- {uniname2ctype_offset(str285), 533},
+ {uniname2ctype_offset(str285), 537},
{-1},
{uniname2ctype_offset(str287), 144},
{uniname2ctype_offset(str288), 144},
- {uniname2ctype_offset(str289), 555},
+ {uniname2ctype_offset(str289), 560},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str293), 218},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str297), 212},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str301), 392},
+ {uniname2ctype_offset(str301), 395},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str311), 322},
+ {uniname2ctype_offset(str311), 325},
{-1},
- {uniname2ctype_offset(str313), 453},
+ {uniname2ctype_offset(str313), 456},
{-1},
- {uniname2ctype_offset(str315), 241},
+ {uniname2ctype_offset(str315), 243},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str320), 269},
+ {uniname2ctype_offset(str320), 271},
{-1},
{uniname2ctype_offset(str322), 129},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str328), 331},
+ {uniname2ctype_offset(str328), 334},
{-1}, {-1},
{uniname2ctype_offset(str331), 76},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str335), 556},
+ {uniname2ctype_offset(str335), 561},
{-1}, {-1},
- {uniname2ctype_offset(str338), 329},
+ {uniname2ctype_offset(str338), 332},
{-1},
{uniname2ctype_offset(str340), 76},
{-1},
- {uniname2ctype_offset(str342), 346},
+ {uniname2ctype_offset(str342), 349},
{-1}, {-1},
{uniname2ctype_offset(str345), 53},
- {uniname2ctype_offset(str346), 268},
+ {uniname2ctype_offset(str346), 270},
{-1},
- {uniname2ctype_offset(str348), 423},
+ {uniname2ctype_offset(str348), 426},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str352), 529},
+ {uniname2ctype_offset(str352), 533},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str362), 163},
{-1}, {-1}, {-1},
@@ -42802,14 +43893,14 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str373), 160},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str381), 550},
+ {uniname2ctype_offset(str381), 554},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str386), 368},
+ {uniname2ctype_offset(str386), 371},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str399), 327},
+ {uniname2ctype_offset(str399), 330},
{-1},
- {uniname2ctype_offset(str401), 544},
+ {uniname2ctype_offset(str401), 548},
{-1}, {-1},
{uniname2ctype_offset(str404), 81},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -42832,176 +43923,179 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1},
{uniname2ctype_offset(str470), 22},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str475), 521},
+ {uniname2ctype_offset(str475), 524},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str480), 454},
+ {uniname2ctype_offset(str480), 457},
{uniname2ctype_offset(str481), 188},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str486), 473},
+ {uniname2ctype_offset(str486), 476},
{-1},
- {uniname2ctype_offset(str488), 582},
+ {uniname2ctype_offset(str488), 588},
{-1}, {-1},
- {uniname2ctype_offset(str491), 467},
+ {uniname2ctype_offset(str491), 470},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str500), 127},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str504), 187},
- {uniname2ctype_offset(str505), 247},
+ {uniname2ctype_offset(str505), 249},
{uniname2ctype_offset(str506), 24},
{-1}, {-1},
{uniname2ctype_offset(str509), 24},
{-1},
- {uniname2ctype_offset(str511), 460},
+ {uniname2ctype_offset(str511), 463},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str520), 420},
+ {uniname2ctype_offset(str520), 423},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str533), 230},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str538), 91},
{-1}, {-1},
- {uniname2ctype_offset(str541), 549},
+ {uniname2ctype_offset(str541), 553},
{-1},
{uniname2ctype_offset(str543), 91},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str556), 542},
+ {uniname2ctype_offset(str556), 546},
{-1},
- {uniname2ctype_offset(str558), 347},
+ {uniname2ctype_offset(str558), 350},
{uniname2ctype_offset(str559), 70},
- {uniname2ctype_offset(str560), 512},
+ {uniname2ctype_offset(str560), 515},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str565), 615},
+ {uniname2ctype_offset(str565), 624},
{uniname2ctype_offset(str566), 37},
{-1},
{uniname2ctype_offset(str568), 113},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str572), 499},
+ {uniname2ctype_offset(str572), 502},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str577), 602},
+ {uniname2ctype_offset(str577), 611},
{-1},
{uniname2ctype_offset(str579), 106},
{-1}, {-1},
- {uniname2ctype_offset(str582), 403},
- {uniname2ctype_offset(str583), 477},
+ {uniname2ctype_offset(str582), 406},
+ {uniname2ctype_offset(str583), 480},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str590), 74},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str594), 168},
{-1},
- {uniname2ctype_offset(str596), 613},
+ {uniname2ctype_offset(str596), 622},
{uniname2ctype_offset(str597), 146},
{-1}, {-1},
- {uniname2ctype_offset(str600), 487},
+ {uniname2ctype_offset(str600), 490},
{-1},
{uniname2ctype_offset(str602), 70},
{-1},
- {uniname2ctype_offset(str604), 573},
- {uniname2ctype_offset(str605), 620},
+ {uniname2ctype_offset(str604), 579},
+ {uniname2ctype_offset(str605), 629},
{-1}, {-1},
- {uniname2ctype_offset(str608), 628},
+ {uniname2ctype_offset(str608), 637},
{uniname2ctype_offset(str609), 229},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str614), 603},
+ {uniname2ctype_offset(str614), 612},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str624), 195},
- {uniname2ctype_offset(str625), 444},
+ {uniname2ctype_offset(str625), 447},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str630), 29},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str636), 539},
+ {uniname2ctype_offset(str636), 543},
{-1}, {-1},
{uniname2ctype_offset(str639), 49},
{-1}, {-1},
{uniname2ctype_offset(str642), 19},
- {-1}, {-1},
- {uniname2ctype_offset(str645), 482},
+ {uniname2ctype_offset(str643), 564},
+ {-1},
+ {uniname2ctype_offset(str645), 485},
{-1},
{uniname2ctype_offset(str647), 192},
{-1}, {-1},
- {uniname2ctype_offset(str650), 484},
+ {uniname2ctype_offset(str650), 487},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str657), 51},
{-1}, {-1},
- {uniname2ctype_offset(str660), 266},
+ {uniname2ctype_offset(str660), 268},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str672), 332},
+ {uniname2ctype_offset(str672), 335},
{-1}, {-1},
{uniname2ctype_offset(str675), 68},
{-1}, {-1},
{uniname2ctype_offset(str678), 171},
- {uniname2ctype_offset(str679), 599},
+ {uniname2ctype_offset(str679), 607},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str683), 265},
+ {uniname2ctype_offset(str683), 267},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str692), 69},
{-1}, {-1},
- {uniname2ctype_offset(str695), 547},
+ {uniname2ctype_offset(str695), 551},
{uniname2ctype_offset(str696), 175},
- {uniname2ctype_offset(str697), 396},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str703), 523},
+ {uniname2ctype_offset(str697), 399},
+ {-1}, {-1},
+ {uniname2ctype_offset(str700), 236},
+ {-1}, {-1},
+ {uniname2ctype_offset(str703), 527},
{-1},
- {uniname2ctype_offset(str705), 344},
+ {uniname2ctype_offset(str705), 347},
{-1},
{uniname2ctype_offset(str707), 158},
- {uniname2ctype_offset(str708), 581},
+ {uniname2ctype_offset(str708), 587},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str713), 373},
+ {uniname2ctype_offset(str713), 376},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str720), 72},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str737), 7},
- {uniname2ctype_offset(str738), 370},
+ {uniname2ctype_offset(str738), 373},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str742), 6},
{-1}, {-1},
- {uniname2ctype_offset(str745), 267},
+ {uniname2ctype_offset(str745), 269},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str753), 238},
+ {uniname2ctype_offset(str753), 240},
{-1},
- {uniname2ctype_offset(str755), 511},
+ {uniname2ctype_offset(str755), 514},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str760), 428},
+ {uniname2ctype_offset(str760), 431},
{uniname2ctype_offset(str761), 167},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str766), 156},
- {uniname2ctype_offset(str767), 600},
+ {uniname2ctype_offset(str767), 608},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str771), 167},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str776), 266},
+ {uniname2ctype_offset(str776), 268},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str783), 156},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str787), 254},
+ {uniname2ctype_offset(str787), 256},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str791), 193},
{-1}, {-1},
- {uniname2ctype_offset(str794), 583},
+ {uniname2ctype_offset(str794), 589},
{-1}, {-1},
{uniname2ctype_offset(str797), 50},
{-1},
- {uniname2ctype_offset(str799), 608},
+ {uniname2ctype_offset(str799), 617},
{-1}, {-1},
{uniname2ctype_offset(str802), 13},
- {uniname2ctype_offset(str803), 587},
+ {uniname2ctype_offset(str803), 593},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str809), 443},
+ {uniname2ctype_offset(str809), 446},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str814), 490},
+ {uniname2ctype_offset(str814), 493},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str818), 395},
+ {uniname2ctype_offset(str818), 398},
{-1}, {-1},
- {uniname2ctype_offset(str821), 479},
- {uniname2ctype_offset(str822), 589},
+ {uniname2ctype_offset(str821), 482},
+ {uniname2ctype_offset(str822), 595},
{uniname2ctype_offset(str823), 47},
{uniname2ctype_offset(str824), 112},
- {uniname2ctype_offset(str825), 441},
+ {uniname2ctype_offset(str825), 444},
{-1}, {-1},
- {uniname2ctype_offset(str828), 590},
+ {uniname2ctype_offset(str828), 596},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str838), 157},
{-1}, {-1}, {-1},
@@ -43014,32 +44108,32 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str855), 67},
{-1},
- {uniname2ctype_offset(str857), 316},
+ {uniname2ctype_offset(str857), 319},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str875), 366},
+ {uniname2ctype_offset(str875), 369},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str886), 401},
+ {uniname2ctype_offset(str886), 404},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str895), 409},
+ {uniname2ctype_offset(str895), 412},
{-1}, {-1},
- {uniname2ctype_offset(str898), 497},
+ {uniname2ctype_offset(str898), 500},
{-1},
- {uniname2ctype_offset(str900), 612},
+ {uniname2ctype_offset(str900), 621},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str906), 518},
- {uniname2ctype_offset(str907), 446},
+ {uniname2ctype_offset(str906), 521},
+ {uniname2ctype_offset(str907), 449},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str920), 416},
+ {uniname2ctype_offset(str920), 419},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str924), 68},
- {uniname2ctype_offset(str925), 592},
- {uniname2ctype_offset(str926), 341},
+ {uniname2ctype_offset(str925), 599},
+ {uniname2ctype_offset(str926), 344},
{-1},
- {uniname2ctype_offset(str928), 536},
- {uniname2ctype_offset(str929), 458},
+ {uniname2ctype_offset(str928), 540},
+ {uniname2ctype_offset(str929), 461},
{uniname2ctype_offset(str930), 41},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -43048,13 +44142,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str961), 2},
{-1},
- {uniname2ctype_offset(str963), 255},
+ {uniname2ctype_offset(str963), 257},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str974), 507},
+ {uniname2ctype_offset(str974), 510},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str986), 367},
+ {uniname2ctype_offset(str986), 370},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str994), 85},
{uniname2ctype_offset(str995), 104},
@@ -43062,21 +44156,21 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1010), 26},
{-1}, {-1},
- {uniname2ctype_offset(str1013), 492},
+ {uniname2ctype_offset(str1013), 495},
{-1},
- {uniname2ctype_offset(str1015), 481},
+ {uniname2ctype_offset(str1015), 484},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1021), 67},
{-1}, {-1},
{uniname2ctype_offset(str1024), 53},
- {uniname2ctype_offset(str1025), 456},
+ {uniname2ctype_offset(str1025), 459},
{-1}, {-1},
{uniname2ctype_offset(str1028), 136},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1032), 377},
+ {uniname2ctype_offset(str1032), 380},
{-1}, {-1},
- {uniname2ctype_offset(str1035), 319},
- {uniname2ctype_offset(str1036), 563},
+ {uniname2ctype_offset(str1035), 322},
+ {uniname2ctype_offset(str1036), 569},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1049), 173},
@@ -43086,26 +44180,26 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1072), 197},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1081), 411},
+ {uniname2ctype_offset(str1081), 414},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1085), 257},
+ {uniname2ctype_offset(str1085), 259},
{-1},
- {uniname2ctype_offset(str1087), 593},
+ {uniname2ctype_offset(str1087), 600},
{-1},
{uniname2ctype_offset(str1089), 115},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1095), 248},
+ {uniname2ctype_offset(str1095), 250},
{uniname2ctype_offset(str1096), 71},
- {uniname2ctype_offset(str1097), 537},
+ {uniname2ctype_offset(str1097), 541},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1102), 520},
+ {uniname2ctype_offset(str1102), 523},
{-1},
{uniname2ctype_offset(str1104), 228},
{uniname2ctype_offset(str1105), 217},
{-1},
- {uniname2ctype_offset(str1107), 538},
+ {uniname2ctype_offset(str1107), 542},
{-1},
- {uniname2ctype_offset(str1109), 237},
+ {uniname2ctype_offset(str1109), 239},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1114), 69},
{uniname2ctype_offset(str1115), 11},
@@ -43114,7 +44208,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1120), 60},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1134), 422},
+ {uniname2ctype_offset(str1134), 425},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1140), 93},
{-1},
@@ -43122,153 +44216,155 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str1145), 137},
{uniname2ctype_offset(str1146), 131},
- {uniname2ctype_offset(str1147), 264},
+ {uniname2ctype_offset(str1147), 266},
{-1},
{uniname2ctype_offset(str1149), 158},
{uniname2ctype_offset(str1150), 98},
- {uniname2ctype_offset(str1151), 495},
+ {uniname2ctype_offset(str1151), 498},
{uniname2ctype_offset(str1152), 217},
{uniname2ctype_offset(str1153), 138},
{-1}, {-1},
- {uniname2ctype_offset(str1156), 525},
+ {uniname2ctype_offset(str1156), 529},
{uniname2ctype_offset(str1157), 203},
{uniname2ctype_offset(str1158), 166},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1164), 238},
+ {uniname2ctype_offset(str1164), 240},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1168), 104},
{-1},
- {uniname2ctype_offset(str1170), 386},
- {uniname2ctype_offset(str1171), 532},
+ {uniname2ctype_offset(str1170), 389},
+ {uniname2ctype_offset(str1171), 536},
{-1}, {-1},
- {uniname2ctype_offset(str1174), 323},
+ {uniname2ctype_offset(str1174), 326},
{uniname2ctype_offset(str1175), 26},
{uniname2ctype_offset(str1176), 208},
{uniname2ctype_offset(str1177), 74},
- {uniname2ctype_offset(str1178), 350},
+ {uniname2ctype_offset(str1178), 353},
{-1},
{uniname2ctype_offset(str1180), 183},
{uniname2ctype_offset(str1181), 151},
- {uniname2ctype_offset(str1182), 356},
+ {uniname2ctype_offset(str1182), 359},
{uniname2ctype_offset(str1183), 101},
{-1},
{uniname2ctype_offset(str1185), 170},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1190), 270},
- {uniname2ctype_offset(str1191), 541},
+ {uniname2ctype_offset(str1186), 597},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1190), 272},
+ {uniname2ctype_offset(str1191), 545},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1197), 101},
{uniname2ctype_offset(str1198), 135},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1203), 363},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1207), 459},
+ {uniname2ctype_offset(str1203), 366},
+ {uniname2ctype_offset(str1204), 609},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1207), 462},
{-1},
{uniname2ctype_offset(str1209), 186},
{-1}, {-1},
- {uniname2ctype_offset(str1212), 376},
+ {uniname2ctype_offset(str1212), 379},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str1223), 578},
+ {uniname2ctype_offset(str1223), 584},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1227), 241},
+ {uniname2ctype_offset(str1227), 243},
{-1},
{uniname2ctype_offset(str1229), 235},
- {uniname2ctype_offset(str1230), 265},
+ {uniname2ctype_offset(str1230), 267},
{uniname2ctype_offset(str1231), 206},
- {uniname2ctype_offset(str1232), 352},
+ {uniname2ctype_offset(str1232), 355},
{uniname2ctype_offset(str1233), 73},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1234), 296},
- {uniname2ctype_offset(str1235), 298},
- {uniname2ctype_offset(str1236), 295},
- {uniname2ctype_offset(str1237), 297},
+ {uniname2ctype_offset(str1234), 298},
+ {uniname2ctype_offset(str1235), 300},
+ {uniname2ctype_offset(str1236), 297},
+ {uniname2ctype_offset(str1237), 299},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1241), 299},
- {uniname2ctype_offset(str1242), 277},
+ {uniname2ctype_offset(str1241), 301},
+ {uniname2ctype_offset(str1242), 279},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1243), 25},
- {uniname2ctype_offset(str1244), 338},
+ {uniname2ctype_offset(str1244), 341},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1245), 279},
+ {uniname2ctype_offset(str1245), 281},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1246), 432},
+ {uniname2ctype_offset(str1246), 435},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1247), 278},
+ {uniname2ctype_offset(str1247), 280},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1248), 30},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1249), 281},
- {uniname2ctype_offset(str1250), 294},
- {uniname2ctype_offset(str1251), 280},
- {uniname2ctype_offset(str1252), 282},
- {uniname2ctype_offset(str1253), 293},
+ {uniname2ctype_offset(str1249), 283},
+ {uniname2ctype_offset(str1250), 296},
+ {uniname2ctype_offset(str1251), 282},
+ {uniname2ctype_offset(str1252), 284},
+ {uniname2ctype_offset(str1253), 295},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1254), 560},
+ {uniname2ctype_offset(str1254), 566},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1255), 289},
+ {uniname2ctype_offset(str1255), 291},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1256), 276},
+ {uniname2ctype_offset(str1256), 278},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1263), 64},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1257), 288},
- {uniname2ctype_offset(str1258), 290},
- {uniname2ctype_offset(str1259), 292},
+ {uniname2ctype_offset(str1257), 290},
+ {uniname2ctype_offset(str1258), 292},
+ {uniname2ctype_offset(str1259), 303},
+ {uniname2ctype_offset(str1260), 294},
+ {-1},
+ {uniname2ctype_offset(str1262), 293},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {-1}, {-1},
-#ifdef USE_UNICODE_AGE_PROPERTIES
- {uniname2ctype_offset(str1262), 291},
{uniname2ctype_offset(str1263), 64},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
{-1},
- {uniname2ctype_offset(str1265), 286},
+ {uniname2ctype_offset(str1265), 302},
#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1266), 21},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1267), 285},
- {uniname2ctype_offset(str1268), 287},
- {uniname2ctype_offset(str1269), 300},
+ {uniname2ctype_offset(str1267), 288},
{-1},
+ {uniname2ctype_offset(str1269), 287},
+ {uniname2ctype_offset(str1270), 289},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1271), 248},
+ {uniname2ctype_offset(str1271), 250},
+#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {-1},
+ {uniname2ctype_offset(str1273), 286},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
{uniname2ctype_offset(str1274), 200},
#ifndef USE_UNICODE_AGE_PROPERTIES
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#else /* USE_UNICODE_AGE_PROPERTIES */
- {-1}, {-1},
- {uniname2ctype_offset(str1277), 284},
- {-1},
- {uniname2ctype_offset(str1279), 283},
- {-1}, {-1},
+ {uniname2ctype_offset(str1275), 285},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#endif /* USE_UNICODE_AGE_PROPERTIES */
- {uniname2ctype_offset(str1282), 626},
- {-1},
- {uniname2ctype_offset(str1284), 110},
+ {uniname2ctype_offset(str1282), 635},
+ {-1}, {-1},
{uniname2ctype_offset(str1285), 105},
- {-1},
- {uniname2ctype_offset(str1287), 110},
- {-1},
- {uniname2ctype_offset(str1289), 345},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1289), 348},
{-1}, {-1},
- {uniname2ctype_offset(str1292), 394},
+ {uniname2ctype_offset(str1292), 397},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1297), 145},
- {uniname2ctype_offset(str1298), 117},
- {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str1300), 86},
{uniname2ctype_offset(str1301), 141},
{-1}, {-1}, {-1}, {-1},
@@ -43277,14 +44373,11 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1314), 193},
{-1}, {-1},
{uniname2ctype_offset(str1317), 86},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1323), 535},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1327), 117},
- {uniname2ctype_offset(str1328), 227},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str1330), 107},
- {uniname2ctype_offset(str1331), 261},
+ {uniname2ctype_offset(str1328), 227},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1331), 263},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1340), 105},
{-1}, {-1},
@@ -43295,18 +44388,20 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1352), 88},
{-1},
{uniname2ctype_offset(str1354), 164},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1358), 624},
+ {-1},
+ {uniname2ctype_offset(str1356), 605},
+ {-1},
+ {uniname2ctype_offset(str1358), 633},
{-1},
{uniname2ctype_offset(str1360), 3},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1365), 384},
+ {uniname2ctype_offset(str1365), 387},
{-1},
- {uniname2ctype_offset(str1367), 530},
+ {uniname2ctype_offset(str1367), 534},
{-1},
- {uniname2ctype_offset(str1369), 256},
+ {uniname2ctype_offset(str1369), 258},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1373), 272},
+ {uniname2ctype_offset(str1373), 274},
{-1},
{uniname2ctype_offset(str1375), 135},
{-1}, {-1}, {-1}, {-1},
@@ -43319,29 +44414,29 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str1392), 138},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1400), 201},
- {uniname2ctype_offset(str1401), 397},
+ {uniname2ctype_offset(str1401), 400},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1407), 224},
{-1},
{uniname2ctype_offset(str1409), 38},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1415), 570},
+ {uniname2ctype_offset(str1415), 576},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1419), 140},
{uniname2ctype_offset(str1420), 140},
{-1},
- {uniname2ctype_offset(str1422), 321},
+ {uniname2ctype_offset(str1422), 324},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1426), 39},
{-1},
{uniname2ctype_offset(str1428), 181},
{uniname2ctype_offset(str1429), 36},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1438), 434},
+ {uniname2ctype_offset(str1438), 437},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1442), 540},
+ {uniname2ctype_offset(str1442), 544},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1448), 505},
+ {uniname2ctype_offset(str1448), 508},
{uniname2ctype_offset(str1449), 122},
{-1},
{uniname2ctype_offset(str1451), 203},
@@ -43352,109 +44447,106 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str1460), 215},
{-1},
- {uniname2ctype_offset(str1462), 554},
+ {uniname2ctype_offset(str1462), 559},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1470), 504},
- {uniname2ctype_offset(str1471), 506},
+ {uniname2ctype_offset(str1470), 507},
+ {uniname2ctype_offset(str1471), 509},
{-1}, {-1},
{uniname2ctype_offset(str1474), 134},
- {uniname2ctype_offset(str1475), 426},
- {uniname2ctype_offset(str1476), 508},
+ {uniname2ctype_offset(str1475), 429},
+ {uniname2ctype_offset(str1476), 511},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1480), 245},
+ {uniname2ctype_offset(str1480), 247},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1489), 33},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1495), 260},
+ {uniname2ctype_offset(str1495), 262},
{-1},
- {uniname2ctype_offset(str1497), 496},
+ {uniname2ctype_offset(str1497), 499},
{-1},
- {uniname2ctype_offset(str1499), 611},
+ {uniname2ctype_offset(str1499), 620},
{-1},
{uniname2ctype_offset(str1501), 196},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1507), 122},
{uniname2ctype_offset(str1508), 231},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1517), 610},
+ {uniname2ctype_offset(str1517), 619},
{-1}, {-1},
- {uniname2ctype_offset(str1520), 237},
+ {uniname2ctype_offset(str1520), 239},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1524), 483},
+ {uniname2ctype_offset(str1524), 486},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1529), 120},
{-1},
- {uniname2ctype_offset(str1531), 419},
+ {uniname2ctype_offset(str1531), 422},
{-1},
{uniname2ctype_offset(str1533), 142},
{-1}, {-1},
{uniname2ctype_offset(str1536), 127},
- {uniname2ctype_offset(str1537), 269},
+ {uniname2ctype_offset(str1537), 271},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1542), 465},
+ {uniname2ctype_offset(str1542), 468},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1547), 168},
{-1},
- {uniname2ctype_offset(str1549), 519},
+ {uniname2ctype_offset(str1549), 522},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1557), 85},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1563), 273},
+ {uniname2ctype_offset(str1563), 275},
{-1},
- {uniname2ctype_offset(str1565), 326},
+ {uniname2ctype_offset(str1565), 329},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1570), 210},
{-1},
{uniname2ctype_offset(str1572), 115},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1577), 564},
+ {uniname2ctype_offset(str1577), 570},
{-1}, {-1},
{uniname2ctype_offset(str1580), 131},
{-1},
{uniname2ctype_offset(str1582), 219},
{uniname2ctype_offset(str1583), 125},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1587), 546},
+ {uniname2ctype_offset(str1587), 550},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1597), 81},
{uniname2ctype_offset(str1598), 219},
- {-1}, {-1},
- {uniname2ctype_offset(str1601), 202},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1607), 586},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1607), 592},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1613), 164},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1617), 502},
- {uniname2ctype_offset(str1618), 272},
+ {uniname2ctype_offset(str1617), 505},
+ {uniname2ctype_offset(str1618), 274},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1622), 385},
- {uniname2ctype_offset(str1623), 567},
+ {uniname2ctype_offset(str1622), 388},
+ {uniname2ctype_offset(str1623), 573},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1629), 39},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1635), 72},
- {uniname2ctype_offset(str1636), 202},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1643), 62},
{uniname2ctype_offset(str1644), 235},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1654), 400},
- {uniname2ctype_offset(str1655), 274},
+ {uniname2ctype_offset(str1654), 403},
+ {uniname2ctype_offset(str1655), 276},
{-1},
{uniname2ctype_offset(str1657), 114},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1662), 129},
{-1},
- {uniname2ctype_offset(str1664), 448},
+ {uniname2ctype_offset(str1664), 451},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1671), 340},
+ {uniname2ctype_offset(str1671), 343},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1675), 469},
+ {uniname2ctype_offset(str1675), 472},
{-1},
- {uniname2ctype_offset(str1677), 314},
+ {uniname2ctype_offset(str1677), 317},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1682), 625},
+ {uniname2ctype_offset(str1682), 634},
{-1},
{uniname2ctype_offset(str1684), 199},
{-1},
@@ -43463,186 +44555,192 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1691), 124},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1699), 379},
+ {uniname2ctype_offset(str1699), 382},
{-1},
- {uniname2ctype_offset(str1701), 522},
+ {uniname2ctype_offset(str1701), 525},
{-1}, {-1},
{uniname2ctype_offset(str1704), 207},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1714), 207},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1722), 359},
+ {uniname2ctype_offset(str1722), 362},
{-1},
- {uniname2ctype_offset(str1724), 576},
+ {uniname2ctype_offset(str1724), 582},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1728), 221},
- {uniname2ctype_offset(str1729), 442},
+ {uniname2ctype_offset(str1729), 445},
{uniname2ctype_offset(str1730), 222},
- {uniname2ctype_offset(str1731), 534},
- {uniname2ctype_offset(str1732), 247},
+ {uniname2ctype_offset(str1731), 538},
+ {uniname2ctype_offset(str1732), 249},
{uniname2ctype_offset(str1733), 123},
{uniname2ctype_offset(str1734), 114},
- {uniname2ctype_offset(str1735), 258},
+ {uniname2ctype_offset(str1735), 260},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1740), 129},
{-1},
{uniname2ctype_offset(str1742), 161},
{-1}, {-1},
- {uniname2ctype_offset(str1745), 524},
- {uniname2ctype_offset(str1746), 402},
+ {uniname2ctype_offset(str1745), 528},
+ {uniname2ctype_offset(str1746), 405},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str1750), 20},
{-1},
- {uniname2ctype_offset(str1752), 516},
+ {uniname2ctype_offset(str1752), 519},
{uniname2ctype_offset(str1753), 148},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1757), 515},
+ {uniname2ctype_offset(str1757), 518},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1766), 73},
{-1},
{uniname2ctype_offset(str1768), 148},
- {uniname2ctype_offset(str1769), 374},
+ {uniname2ctype_offset(str1769), 377},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1776), 126},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1781), 552},
+ {uniname2ctype_offset(str1781), 556},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1788), 97},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1793), 97},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1806), 348},
+ {uniname2ctype_offset(str1806), 351},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1810), 630},
- {uniname2ctype_offset(str1811), 245},
+ {uniname2ctype_offset(str1810), 640},
+ {uniname2ctype_offset(str1811), 247},
{-1},
- {uniname2ctype_offset(str1813), 264},
+ {uniname2ctype_offset(str1813), 266},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1821), 224},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1828), 399},
+ {uniname2ctype_offset(str1828), 402},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1838), 614},
+ {uniname2ctype_offset(str1838), 623},
{-1}, {-1},
- {uniname2ctype_offset(str1841), 457},
- {uniname2ctype_offset(str1842), 391},
+ {uniname2ctype_offset(str1841), 460},
+ {uniname2ctype_offset(str1842), 394},
{uniname2ctype_offset(str1843), 65},
{-1},
- {uniname2ctype_offset(str1845), 263},
+ {uniname2ctype_offset(str1845), 265},
{-1}, {-1},
{uniname2ctype_offset(str1848), 109},
{-1}, {-1},
{uniname2ctype_offset(str1851), 137},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1857), 244},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1866), 61},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1871), 491},
+ {uniname2ctype_offset(str1871), 494},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1881), 63},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1888), 527},
- {uniname2ctype_offset(str1889), 551},
+ {uniname2ctype_offset(str1888), 531},
+ {uniname2ctype_offset(str1889), 555},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1896), 617},
+ {uniname2ctype_offset(str1896), 626},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1902), 431},
+ {uniname2ctype_offset(str1902), 434},
{-1},
- {uniname2ctype_offset(str1904), 452},
- {uniname2ctype_offset(str1905), 584},
+ {uniname2ctype_offset(str1904), 455},
+ {uniname2ctype_offset(str1905), 590},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1912), 580},
+ {uniname2ctype_offset(str1912), 586},
{uniname2ctype_offset(str1913), 143},
{-1}, {-1},
- {uniname2ctype_offset(str1916), 588},
+ {uniname2ctype_offset(str1916), 594},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1924), 143},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1929), 429},
+ {uniname2ctype_offset(str1929), 432},
{-1}, {-1},
- {uniname2ctype_offset(str1932), 412},
+ {uniname2ctype_offset(str1932), 415},
{-1}, {-1},
- {uniname2ctype_offset(str1935), 262},
- {uniname2ctype_offset(str1936), 335},
+ {uniname2ctype_offset(str1935), 264},
+ {uniname2ctype_offset(str1936), 338},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1941), 27},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1948), 87},
- {-1},
- {uniname2ctype_offset(str1950), 421},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1950), 424},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1956), 250},
- {uniname2ctype_offset(str1957), 87},
+ {uniname2ctype_offset(str1956), 252},
+ {-1},
{uniname2ctype_offset(str1958), 109},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1964), 437},
+ {uniname2ctype_offset(str1964), 440},
{-1}, {-1},
- {uniname2ctype_offset(str1967), 489},
- {uniname2ctype_offset(str1968), 634},
- {uniname2ctype_offset(str1969), 249},
+ {uniname2ctype_offset(str1967), 492},
+ {uniname2ctype_offset(str1968), 644},
+ {uniname2ctype_offset(str1969), 251},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str1974), 355},
+ {uniname2ctype_offset(str1974), 358},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str1981), 176},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str1985), 405},
+ {uniname2ctype_offset(str1985), 408},
{-1}, {-1},
- {uniname2ctype_offset(str1988), 438},
+ {uniname2ctype_offset(str1988), 441},
{-1}, {-1},
{uniname2ctype_offset(str1991), 1},
{-1},
- {uniname2ctype_offset(str1993), 372},
+ {uniname2ctype_offset(str1993), 375},
{uniname2ctype_offset(str1994), 175},
{-1},
{uniname2ctype_offset(str1996), 42},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2002), 591},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2006), 407},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2010), 375},
- {uniname2ctype_offset(str2011), 267},
- {uniname2ctype_offset(str2012), 559},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2001), 110},
+ {uniname2ctype_offset(str2002), 598},
{-1},
- {uniname2ctype_offset(str2014), 333},
+ {uniname2ctype_offset(str2004), 110},
+ {-1},
+ {uniname2ctype_offset(str2006), 410},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2010), 378},
+ {uniname2ctype_offset(str2011), 269},
+ {uniname2ctype_offset(str2012), 565},
{-1},
+ {uniname2ctype_offset(str2014), 336},
+ {uniname2ctype_offset(str2015), 117},
{uniname2ctype_offset(str2016), 209},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2023), 32},
- {uniname2ctype_offset(str2024), 503},
+ {uniname2ctype_offset(str2024), 506},
{-1},
{uniname2ctype_offset(str2026), 176},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2033), 8},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2040), 539},
+ {-1},
{uniname2ctype_offset(str2042), 121},
{uniname2ctype_offset(str2043), 17},
+ {uniname2ctype_offset(str2044), 117},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2047), 107},
+ {uniname2ctype_offset(str2048), 526},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2062), 130},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2070), 9},
{-1}, {-1},
- {uniname2ctype_offset(str2073), 342},
+ {uniname2ctype_offset(str2073), 345},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2077), 150},
- {uniname2ctype_offset(str2078), 254},
+ {uniname2ctype_offset(str2078), 256},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str2090), 275},
+ {uniname2ctype_offset(str2090), 277},
{-1},
{uniname2ctype_offset(str2092), 150},
{-1}, {-1},
- {uniname2ctype_offset(str2095), 353},
+ {uniname2ctype_offset(str2095), 356},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2103), 162},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2110), 123},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2114), 436},
- {uniname2ctype_offset(str2115), 381},
+ {uniname2ctype_offset(str2114), 439},
+ {uniname2ctype_offset(str2115), 384},
{-1},
{uniname2ctype_offset(str2117), 174},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -43653,15 +44751,15 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1},
{uniname2ctype_offset(str2137), 12},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2141), 242},
+ {uniname2ctype_offset(str2141), 244},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2145), 52},
- {uniname2ctype_offset(str2146), 413},
+ {uniname2ctype_offset(str2146), 416},
{-1}, {-1},
{uniname2ctype_offset(str2149), 221},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2163), 249},
+ {uniname2ctype_offset(str2163), 251},
{-1},
{uniname2ctype_offset(str2165), 174},
{uniname2ctype_offset(str2166), 5},
@@ -43674,107 +44772,100 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str2182), 216},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2195), 351},
- {uniname2ctype_offset(str2196), 493},
+ {uniname2ctype_offset(str2195), 354},
+ {uniname2ctype_offset(str2196), 496},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2207), 196},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2211), 20},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2216), 364},
+ {uniname2ctype_offset(str2216), 367},
{uniname2ctype_offset(str2217), 128},
- {uniname2ctype_offset(str2218), 543},
+ {uniname2ctype_offset(str2218), 547},
{uniname2ctype_offset(str2219), 78},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2224), 450},
+ {uniname2ctype_offset(str2224), 453},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2228), 43},
{uniname2ctype_offset(str2229), 35},
{-1}, {-1},
- {uniname2ctype_offset(str2232), 382},
+ {uniname2ctype_offset(str2232), 385},
{uniname2ctype_offset(str2233), 216},
{uniname2ctype_offset(str2234), 92},
- {uniname2ctype_offset(str2235), 486},
+ {uniname2ctype_offset(str2235), 489},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2241), 92},
{-1}, {-1},
- {uniname2ctype_offset(str2244), 440},
+ {uniname2ctype_offset(str2244), 443},
{-1}, {-1},
{uniname2ctype_offset(str2247), 36},
- {uniname2ctype_offset(str2248), 595},
+ {uniname2ctype_offset(str2248), 602},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2276), 415},
+ {uniname2ctype_offset(str2276), 418},
{-1},
{uniname2ctype_offset(str2278), 124},
{uniname2ctype_offset(str2279), 192},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2283), 214},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2296), 639},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2305), 205},
{-1}, {-1},
{uniname2ctype_offset(str2308), 234},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2312), 177},
{-1}, {-1},
- {uniname2ctype_offset(str2315), 594},
- {uniname2ctype_offset(str2316), 629},
- {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2315), 601},
+ {uniname2ctype_offset(str2316), 638},
+ {-1},
+ {uniname2ctype_offset(str2318), 202},
+ {-1}, {-1},
{uniname2ctype_offset(str2321), 153},
{-1},
{uniname2ctype_offset(str2323), 172},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2327), 378},
+ {uniname2ctype_offset(str2327), 381},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2331), 627},
+ {uniname2ctype_offset(str2331), 636},
{-1},
{uniname2ctype_offset(str2333), 134},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2341), 185},
- {-1},
- {uniname2ctype_offset(str2343), 585},
- {uniname2ctype_offset(str2344), 439},
- {uniname2ctype_offset(str2345), 239},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2350), 185},
- {uniname2ctype_offset(str2351), 99},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2361), 598},
- {-1}, {-1},
- {uniname2ctype_offset(str2364), 320},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2370), 339},
+ {uniname2ctype_offset(str2343), 591},
+ {uniname2ctype_offset(str2344), 442},
+ {uniname2ctype_offset(str2345), 241},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2353), 202},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2361), 606},
{-1}, {-1},
+ {uniname2ctype_offset(str2364), 323},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2373), 37},
- {uniname2ctype_offset(str2374), 389},
+ {uniname2ctype_offset(str2374), 392},
{uniname2ctype_offset(str2375), 197},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2389), 557},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2397), 418},
- {uniname2ctype_offset(str2398), 410},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2397), 421},
+ {uniname2ctype_offset(str2398), 413},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2405), 165},
- {uniname2ctype_offset(str2406), 107},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2414), 82},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2418), 449},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2427), 23},
- {uniname2ctype_offset(str2428), 183},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2433), 336},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2433), 339},
{uniname2ctype_offset(str2434), 154},
{-1}, {-1},
{uniname2ctype_offset(str2437), 194},
- {uniname2ctype_offset(str2438), 182},
- {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str2440), 100},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
@@ -43782,36 +44873,36 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str2454), 220},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2460), 255},
+ {uniname2ctype_offset(str2460), 257},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str2464), 159},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2470), 445},
+ {uniname2ctype_offset(str2470), 448},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2475), 142},
- {uniname2ctype_offset(str2476), 430},
+ {uniname2ctype_offset(str2476), 558},
{-1},
- {uniname2ctype_offset(str2478), 414},
- {uniname2ctype_offset(str2479), 358},
+ {uniname2ctype_offset(str2478), 417},
+ {uniname2ctype_offset(str2479), 361},
{-1},
- {uniname2ctype_offset(str2481), 383},
+ {uniname2ctype_offset(str2481), 386},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2500), 121},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2506), 528},
+ {uniname2ctype_offset(str2506), 532},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2511), 242},
+ {uniname2ctype_offset(str2511), 244},
{-1},
- {uniname2ctype_offset(str2513), 621},
+ {uniname2ctype_offset(str2513), 630},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2530), 82},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2534), 337},
+ {uniname2ctype_offset(str2534), 340},
+ {uniname2ctype_offset(str2535), 237},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {uniname2ctype_offset(str2545), 275},
+ {uniname2ctype_offset(str2545), 277},
{-1}, {-1},
{uniname2ctype_offset(str2548), 118},
{uniname2ctype_offset(str2549), 159},
@@ -43819,74 +44910,74 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str2551), 89},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2564), 369},
+ {uniname2ctype_offset(str2564), 372},
{-1}, {-1},
- {uniname2ctype_offset(str2567), 315},
+ {uniname2ctype_offset(str2567), 318},
{-1}, {-1},
- {uniname2ctype_offset(str2570), 488},
+ {uniname2ctype_offset(str2570), 491},
{-1},
- {uniname2ctype_offset(str2572), 470},
- {-1}, {-1},
- {uniname2ctype_offset(str2575), 471},
- {uniname2ctype_offset(str2576), 273},
+ {uniname2ctype_offset(str2572), 473},
+ {-1},
+ {uniname2ctype_offset(str2574), 246},
+ {uniname2ctype_offset(str2575), 474},
+ {uniname2ctype_offset(str2576), 275},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2582), 388},
+ {uniname2ctype_offset(str2582), 391},
{-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2588), 154},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2602), 210},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2606), 371},
+ {uniname2ctype_offset(str2606), 374},
{-1},
- {uniname2ctype_offset(str2608), 455},
+ {uniname2ctype_offset(str2608), 458},
{uniname2ctype_offset(str2609), 55},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2625), 260},
+ {uniname2ctype_offset(str2625), 262},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2648), 252},
+ {uniname2ctype_offset(str2648), 254},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2652), 380},
+ {uniname2ctype_offset(str2652), 383},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2656), 365},
+ {uniname2ctype_offset(str2656), 368},
{uniname2ctype_offset(str2657), 112},
- {-1}, {-1},
- {uniname2ctype_offset(str2660), 147},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2665), 87},
{uniname2ctype_offset(str2666), 50},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2670), 623},
+ {uniname2ctype_offset(str2670), 632},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2674), 87},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2688), 139},
- {uniname2ctype_offset(str2689), 631},
+ {uniname2ctype_offset(str2689), 641},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2694), 48},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2705), 478},
+ {uniname2ctype_offset(str2705), 481},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
{uniname2ctype_offset(str2726), 226},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str2738), 225},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str2750), 553},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2750), 557},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2761), 18},
- {uniname2ctype_offset(str2762), 263},
+ {uniname2ctype_offset(str2762), 265},
{-1}, {-1},
{uniname2ctype_offset(str2765), 133},
{uniname2ctype_offset(str2766), 54},
{-1}, {-1},
- {uniname2ctype_offset(str2769), 480},
+ {uniname2ctype_offset(str2769), 483},
{-1}, {-1},
{uniname2ctype_offset(str2772), 199},
{-1}, {-1},
@@ -43894,94 +44985,87 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str2786), 4},
- {uniname2ctype_offset(str2787), 80},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2793), 34},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2801), 29},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2808), 509},
+ {uniname2ctype_offset(str2808), 512},
{-1},
{uniname2ctype_offset(str2810), 226},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2816), 307},
+ {uniname2ctype_offset(str2816), 310},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2821), 390},
+ {uniname2ctype_offset(str2821), 393},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2830), 605},
+ {uniname2ctype_offset(str2830), 614},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2834), 361},
+ {uniname2ctype_offset(str2834), 364},
{-1},
{uniname2ctype_offset(str2836), 78},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2847), 246},
+ {uniname2ctype_offset(str2847), 248},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2852), 302},
+ {uniname2ctype_offset(str2852), 305},
{-1}, {-1},
{uniname2ctype_offset(str2855), 190},
{uniname2ctype_offset(str2856), 66},
{-1}, {-1},
- {uniname2ctype_offset(str2859), 424},
+ {uniname2ctype_offset(str2859), 427},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2867), 306},
- {uniname2ctype_offset(str2868), 246},
+ {uniname2ctype_offset(str2867), 309},
+ {uniname2ctype_offset(str2868), 248},
{-1}, {-1},
- {uniname2ctype_offset(str2871), 252},
+ {uniname2ctype_offset(str2871), 254},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2879), 204},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2886), 622},
+ {uniname2ctype_offset(str2886), 631},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2901), 99},
- {uniname2ctype_offset(str2902), 474},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2902), 477},
{-1},
- {uniname2ctype_offset(str2904), 461},
- {uniname2ctype_offset(str2905), 271},
+ {uniname2ctype_offset(str2904), 464},
+ {uniname2ctype_offset(str2905), 273},
{-1},
- {uniname2ctype_offset(str2907), 271},
+ {uniname2ctype_offset(str2907), 273},
{-1},
- {uniname2ctype_offset(str2909), 571},
+ {uniname2ctype_offset(str2909), 577},
{-1},
- {uniname2ctype_offset(str2911), 447},
+ {uniname2ctype_offset(str2911), 450},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str2941), 360},
+ {uniname2ctype_offset(str2941), 363},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
{uniname2ctype_offset(str2953), 198},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str2964), 517},
+ {uniname2ctype_offset(str2964), 520},
{-1},
- {uniname2ctype_offset(str2966), 236},
+ {uniname2ctype_offset(str2966), 238},
{uniname2ctype_offset(str2967), 43},
- {-1},
- {uniname2ctype_offset(str2969), 204},
+ {-1}, {-1},
{uniname2ctype_offset(str2970), 88},
- {-1},
- {uniname2ctype_offset(str2972), 244},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str2980), 195},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str2984), 568},
+ {uniname2ctype_offset(str2984), 574},
{-1},
{uniname2ctype_offset(str2986), 118},
{uniname2ctype_offset(str2987), 54},
- {uniname2ctype_offset(str2988), 468},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2993), 201},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str2997), 596},
+ {uniname2ctype_offset(str2988), 471},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2997), 603},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3001), 180},
{uniname2ctype_offset(str3002), 64},
{-1}, {-1},
- {uniname2ctype_offset(str3005), 472},
- {uniname2ctype_offset(str3006), 577},
+ {uniname2ctype_offset(str3005), 475},
+ {uniname2ctype_offset(str3006), 583},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3015), 83},
{-1}, {-1},
@@ -43989,60 +45073,73 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3019), 165},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3026), 83},
- {uniname2ctype_offset(str3027), 500},
+ {uniname2ctype_offset(str3027), 503},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3035), 232},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3042), 211},
{-1}, {-1},
- {uniname2ctype_offset(str3045), 606},
+ {uniname2ctype_offset(str3045), 615},
{-1}, {-1},
{uniname2ctype_offset(str3048), 119},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3052), 89},
{uniname2ctype_offset(str3053), 229},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3058), 185},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3067), 185},
+ {uniname2ctype_offset(str3068), 99},
+ {-1}, {-1},
{uniname2ctype_offset(str3071), 198},
{uniname2ctype_offset(str3072), 133},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3082), 130},
- {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3084), 237},
+ {-1},
{uniname2ctype_offset(str3086), 84},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3091), 259},
+ {uniname2ctype_offset(str3087), 342},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3091), 261},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3099), 475},
+ {uniname2ctype_offset(str3099), 478},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3104), 84},
- {-1}, {-1},
- {uniname2ctype_offset(str3107), 310},
{-1},
- {uniname2ctype_offset(str3109), 312},
+ {uniname2ctype_offset(str3106), 562},
+ {uniname2ctype_offset(str3107), 313},
+ {-1},
+ {uniname2ctype_offset(str3109), 315},
{uniname2ctype_offset(str3110), 152},
{uniname2ctype_offset(str3111), 191},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3118), 90},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3123), 107},
+ {-1},
{uniname2ctype_offset(str3125), 191},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3140), 607},
- {-1},
- {uniname2ctype_offset(str3142), 579},
+ {uniname2ctype_offset(str3135), 452},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3142), 585},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3145), 183},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3155), 182},
+ {-1},
{uniname2ctype_offset(str3157), 231},
- {uniname2ctype_offset(str3158), 325},
+ {uniname2ctype_offset(str3158), 328},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3176), 393},
+ {uniname2ctype_offset(str3176), 396},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str3180), 328},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3180), 331},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3193), 433},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3209), 62},
{-1}, {-1},
{uniname2ctype_offset(str3212), 211},
@@ -44050,97 +45147,97 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3215), 180},
{-1}, {-1},
{uniname2ctype_offset(str3218), 125},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3227), 433},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3237), 387},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3237), 390},
{-1},
- {uniname2ctype_offset(str3239), 305},
- {uniname2ctype_offset(str3240), 301},
+ {uniname2ctype_offset(str3239), 308},
+ {uniname2ctype_offset(str3240), 304},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3248), 213},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3254), 250},
+ {uniname2ctype_offset(str3254), 252},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str3265), 601},
+ {uniname2ctype_offset(str3265), 610},
{-1},
{uniname2ctype_offset(str3267), 28},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3275), 146},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3284), 609},
+ {uniname2ctype_offset(str3284), 618},
{-1}, {-1},
{uniname2ctype_offset(str3287), 223},
- {-1}, {-1},
- {uniname2ctype_offset(str3290), 451},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3297), 308},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3297), 311},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3303), 304},
+ {uniname2ctype_offset(str3303), 307},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3313), 253},
+ {uniname2ctype_offset(str3313), 255},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3320), 223},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3328), 63},
- {uniname2ctype_offset(str3329), 616},
+ {uniname2ctype_offset(str3329), 625},
{-1},
{uniname2ctype_offset(str3331), 222},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3338), 262},
+ {uniname2ctype_offset(str3338), 264},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3348), 404},
+ {uniname2ctype_offset(str3348), 407},
{-1},
- {uniname2ctype_offset(str3350), 498},
+ {uniname2ctype_offset(str3350), 501},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3355), 47},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3360), 334},
+ {uniname2ctype_offset(str3360), 337},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3371), 126},
{uniname2ctype_offset(str3372), 16},
- {uniname2ctype_offset(str3373), 251},
+ {uniname2ctype_offset(str3373), 253},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3377), 147},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3391), 236},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3396), 604},
+ {uniname2ctype_offset(str3391), 238},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3396), 613},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3405), 466},
+ {uniname2ctype_offset(str3405), 469},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3416), 51},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3428), 251},
+ {uniname2ctype_offset(str3428), 253},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3433), 205},
{-1}, {-1},
{uniname2ctype_offset(str3436), 213},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3452), 220},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3455), 225},
+ {-1}, {-1},
{uniname2ctype_offset(str3458), 10},
{-1},
- {uniname2ctype_offset(str3460), 618},
+ {uniname2ctype_offset(str3460), 627},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3483), 243},
+ {uniname2ctype_offset(str3483), 245},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3502), 49},
- {-1}, {-1},
- {uniname2ctype_offset(str3505), 476},
+ {-1},
+ {uniname2ctype_offset(str3504), 80},
+ {uniname2ctype_offset(str3505), 479},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3511), 408},
+ {uniname2ctype_offset(str3511), 411},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3532), 572},
+ {uniname2ctype_offset(str3532), 578},
{uniname2ctype_offset(str3533), 57},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44156,11 +45253,13 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3613), 41},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3618), 99},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3648), 462},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3648), 465},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
{uniname2ctype_offset(str3659), 178},
@@ -44168,18 +45267,23 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3664), 177},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3698), 501},
- {uniname2ctype_offset(str3699), 548},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3686), 204},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3689), 246},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3698), 504},
+ {uniname2ctype_offset(str3699), 552},
{-1},
{uniname2ctype_offset(str3701), 96},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str3705), 317},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3705), 320},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3710), 201},
+ {-1}, {-1},
{uniname2ctype_offset(str3713), 96},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3721), 435},
+ {uniname2ctype_offset(str3721), 438},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
@@ -44188,57 +45292,59 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str3754), 71},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str3766), 464},
- {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3770), 147},
+ {uniname2ctype_offset(str3766), 467},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3799), 214},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3807), 258},
+ {uniname2ctype_offset(str3807), 260},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3842), 182},
{-1}, {-1},
- {uniname2ctype_offset(str3845), 257},
+ {uniname2ctype_offset(str3845), 259},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3850), 243},
+ {uniname2ctype_offset(str3850), 245},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3854), 65},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3857), 616},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3868), 259},
+ {-1},
+ {uniname2ctype_offset(str3868), 261},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str3881), 228},
{-1}, {-1},
- {uniname2ctype_offset(str3884), 303},
+ {uniname2ctype_offset(str3884), 306},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3891), 30},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3924), 417},
+ {uniname2ctype_offset(str3924), 420},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str3942), 100},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3949), 274},
+ {-1},
+ {uniname2ctype_offset(str3944), 436},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3949), 276},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str3976), 597},
- {-1},
- {uniname2ctype_offset(str3978), 80},
+ {uniname2ctype_offset(str3976), 604},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {uniname2ctype_offset(str3998), 427},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3998), 430},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4007), 454},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44246,13 +45352,10 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4066), 313},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4106), 45},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4116), 132},
@@ -44263,14 +45366,15 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4169), 220},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4231), 153},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44281,18 +45385,17 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4308), 398},
+ {uniname2ctype_offset(str4308), 401},
{uniname2ctype_offset(str4309), 162},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {uniname2ctype_offset(str4321), 240},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4348), 565},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str4348), 571},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str4361), 566},
+ {uniname2ctype_offset(str4361), 572},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44308,6 +45411,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4487), 147},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44320,42 +45424,45 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4604), 116},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
{uniname2ctype_offset(str4626), 116},
- {uniname2ctype_offset(str4627), 253},
+ {uniname2ctype_offset(str4627), 255},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1},
- {uniname2ctype_offset(str4674), 632},
+ {uniname2ctype_offset(str4674), 642},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4683), 463},
+ {uniname2ctype_offset(str4683), 466},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str4695), 80},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
{uniname2ctype_offset(str4734), 179},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1},
- {uniname2ctype_offset(str4755), 309},
- {uniname2ctype_offset(str4756), 311},
+ {uniname2ctype_offset(str4755), 312},
+ {uniname2ctype_offset(str4756), 314},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str4772), 406},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4772), 409},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str4783), 316},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{uniname2ctype_offset(str4810), 90},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44380,7 +45487,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1},
{uniname2ctype_offset(str4982), 232},
{-1}, {-1}, {-1},
- {uniname2ctype_offset(str4986), 425},
+ {uniname2ctype_offset(str4986), 428},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44388,6 +45495,8 @@ uniname2ctype_p (register const char *str, register size_t len)
{uniname2ctype_offset(str5018), 46},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str5038), 242},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -44398,8 +45507,8 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str5134), 619},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str5134), 628},
#endif /* USE_UNICODE_PROPERTIES */
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#ifndef USE_UNICODE_PROPERTIES
@@ -44529,7 +45638,7 @@ uniname2ctype_p (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {uniname2ctype_offset(str6098), 633}
+ {uniname2ctype_offset(str6098), 643}
#endif /* USE_UNICODE_PROPERTIES */
};
@@ -44560,22 +45669,22 @@ uniname2ctype(const UChar *name, unsigned int len)
return -1;
}
#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_VERSION_MAJOR == 15 && \
ONIG_UNICODE_VERSION_MINOR == 0 && \
ONIG_UNICODE_VERSION_TEENY == 0 && \
1)
# error ONIG_UNICODE_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_VERSION_STRING "14.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 14
+#define ONIG_UNICODE_VERSION_STRING "15.0.0"
+#define ONIG_UNICODE_VERSION_MAJOR 15
#define ONIG_UNICODE_VERSION_MINOR 0
#define ONIG_UNICODE_VERSION_TEENY 0
#if defined ONIG_UNICODE_EMOJI_VERSION_STRING && !( \
- ONIG_UNICODE_EMOJI_VERSION_MAJOR == 14 && \
+ ONIG_UNICODE_EMOJI_VERSION_MAJOR == 15 && \
ONIG_UNICODE_EMOJI_VERSION_MINOR == 0 && \
1)
# error ONIG_UNICODE_EMOJI_VERSION_STRING mismatch
#endif
-#define ONIG_UNICODE_EMOJI_VERSION_STRING "14.0"
-#define ONIG_UNICODE_EMOJI_VERSION_MAJOR 14
+#define ONIG_UNICODE_EMOJI_VERSION_STRING "15.0"
+#define ONIG_UNICODE_EMOJI_VERSION_MAJOR 15
#define ONIG_UNICODE_EMOJI_VERSION_MINOR 0
diff --git a/enc/utf_16_32.h b/enc/utf_16_32.h
index 9f9216d8ff..4d669019bf 100644
--- a/enc/utf_16_32.h
+++ b/enc/utf_16_32.h
@@ -1,5 +1,5 @@
#include "regenc.h"
/* dummy for unsupported, stateful encoding */
-#define ENC_DUMMY_UNICODE(name) ENC_REPLICATE(name, name "BE")
+#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name)
ENC_DUMMY_UNICODE("UTF-16");
ENC_DUMMY_UNICODE("UTF-32");
diff --git a/encoding.c b/encoding.c
index b8fedfb797..2f4b47bdfa 100644
--- a/encoding.c
+++ b/encoding.c
@@ -17,6 +17,7 @@
#include "internal.h"
#include "internal/enc.h"
#include "internal/encoding.h"
+#include "internal/error.h"
#include "internal/inits.h"
#include "internal/load.h"
#include "internal/object.h"
@@ -49,16 +50,14 @@ void rb_encdb_declare(const char *name);
int rb_encdb_replicate(const char *name, const char *orig);
int rb_encdb_dummy(const char *name);
int rb_encdb_alias(const char *alias, const char *orig);
-void rb_encdb_set_unicode(int index);
#pragma GCC visibility pop
#endif
static ID id_encoding;
VALUE rb_cEncoding;
-#define DEFAULT_ENCODING_LIST_CAPA 128
-static VALUE rb_default_encoding_list;
-static VALUE rb_additional_encoding_list;
+#define ENCODING_LIST_CAPA 256
+static VALUE rb_encoding_list;
struct rb_encoding_entry {
const char *name;
@@ -67,9 +66,8 @@ struct rb_encoding_entry {
};
static struct enc_table {
- struct rb_encoding_entry *list;
+ struct rb_encoding_entry list[ENCODING_LIST_CAPA];
int count;
- int size;
st_table *names;
} global_enc_table;
@@ -128,47 +126,25 @@ enc_new(rb_encoding *encoding)
static void
enc_list_update(int index, rb_raw_encoding *encoding)
{
- if (index < DEFAULT_ENCODING_LIST_CAPA) {
- VALUE list = rb_default_encoding_list;
- if (list && NIL_P(rb_ary_entry(list, index))) {
- /* initialize encoding data */
- rb_ary_store(list, index, enc_new(encoding));
- }
- }
- else {
- RB_VM_LOCK_ENTER();
- {
- VALUE list = rb_additional_encoding_list;
- if (list && NIL_P(rb_ary_entry(list, index))) {
- /* initialize encoding data */
- rb_ary_store(list, index - DEFAULT_ENCODING_LIST_CAPA, enc_new(encoding));
- }
- }
- RB_VM_LOCK_LEAVE();
+ RUBY_ASSERT(index < ENCODING_LIST_CAPA);
+
+ VALUE list = rb_encoding_list;
+ if (list && NIL_P(rb_ary_entry(list, index))) {
+ /* initialize encoding data */
+ rb_ary_store(list, index, enc_new(encoding));
}
}
static VALUE
enc_list_lookup(int idx)
{
- VALUE list, enc;
+ VALUE list, enc = Qnil;
- if (idx < DEFAULT_ENCODING_LIST_CAPA) {
- if (!(list = rb_default_encoding_list)) {
- rb_bug("rb_enc_from_encoding_index(%d): no rb_default_encoding_list", idx);
- }
+ if (idx < ENCODING_LIST_CAPA) {
+ list = rb_encoding_list;
+ RUBY_ASSERT(list);
enc = rb_ary_entry(list, idx);
}
- else {
- RB_VM_LOCK_ENTER();
- {
- if (!(list = rb_additional_encoding_list)) {
- rb_bug("rb_enc_from_encoding_index(%d): no rb_additional_encoding_list", idx);
- }
- enc = rb_ary_entry(list, idx - DEFAULT_ENCODING_LIST_CAPA);
- }
- RB_VM_LOCK_LEAVE();
- }
if (NIL_P(enc)) {
rb_bug("rb_enc_from_encoding_index(%d): not created yet", idx);
@@ -345,16 +321,10 @@ rb_find_encoding(VALUE enc)
static int
enc_table_expand(struct enc_table *enc_table, int newsize)
{
- struct rb_encoding_entry *ent;
- int count = newsize;
-
- if (enc_table->size >= newsize) return newsize;
- newsize = (newsize + 7) / 8 * 8;
- ent = REALLOC_N(enc_table->list, struct rb_encoding_entry, newsize);
- memset(ent + enc_table->size, 0, sizeof(*ent)*(newsize - enc_table->size));
- enc_table->list = ent;
- enc_table->size = newsize;
- return count;
+ if (newsize > ENCODING_LIST_CAPA) {
+ rb_raise(rb_eEncodingError, "too many encoding (> %d)", ENCODING_LIST_CAPA);
+ }
+ return newsize;
}
static int
@@ -413,17 +383,7 @@ enc_from_index(struct enc_table *enc_table, int index)
rb_encoding *
rb_enc_from_index(int index)
{
- rb_encoding *enc;
-
- switch (index) {
- case ENCINDEX_ASCII_8BIT: return global_enc_ascii;
- case ENCINDEX_UTF_8: return global_enc_utf_8;
- case ENCINDEX_US_ASCII: return global_enc_us_ascii;
- default:
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- enc = enc_from_index(enc_table, index));
- return enc;
- }
+ return enc_from_index(&global_enc_table, index);
}
int
@@ -462,7 +422,7 @@ enc_registered(struct enc_table *enc_table, const char *name)
st_data_t idx = 0;
if (!name) return -1;
- if (!enc_table->list) return -1;
+ if (!enc_table->names) return -1;
if (st_lookup(enc_table->names, (st_data_t)name, &idx)) {
return (int)idx;
}
@@ -484,11 +444,14 @@ rb_encdb_declare(const char *name)
}
static void
-enc_check_duplication(struct enc_table *enc_table, const char *name)
+enc_check_addable(struct enc_table *enc_table, const char *name)
{
if (enc_registered(enc_table, name) >= 0) {
rb_raise(rb_eArgError, "encoding %s is already registered", name);
}
+ else if (!valid_encoding_name_p(name)) {
+ rb_raise(rb_eArgError, "invalid encoding name: %s", name);
+ }
}
static rb_encoding*
@@ -524,11 +487,7 @@ rb_enc_set_base(const char *name, const char *orig)
int
rb_enc_set_dummy(int index)
{
- rb_encoding *enc;
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- enc = enc_table->list[index].enc);
-
+ rb_encoding *enc = global_enc_table.list[index].enc;
ENC_SET_DUMMY((rb_raw_encoding *)enc);
return index;
}
@@ -538,7 +497,7 @@ enc_replicate(struct enc_table *enc_table, const char *name, rb_encoding *encodi
{
int idx;
- enc_check_duplication(enc_table, name);
+ enc_check_addable(enc_table, name);
idx = enc_register(enc_table, name, encoding);
if (idx < 0) rb_raise(rb_eArgError, "invalid encoding name: %s", name);
set_base_encoding(enc_table, idx, encoding);
@@ -569,7 +528,10 @@ rb_enc_replicate(const char *name, rb_encoding *encoding)
static VALUE
enc_replicate_m(VALUE encoding, VALUE name)
{
- int idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding));
+ int idx;
+ rb_warn_deprecated_to_remove("3.3", "Encoding#replicate", "the original encoding");
+
+ idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding));
RB_GC_GUARD(name);
return rb_enc_from_encoding_index(idx);
}
@@ -724,7 +686,7 @@ rb_enc_alias(const char *alias, const char *orig)
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
- enc_check_duplication(enc_table, alias);
+ enc_check_addable(enc_table, alias);
if ((idx = rb_enc_find_index(orig)) < 0) {
r = -1;
}
@@ -756,20 +718,12 @@ rb_encdb_alias(const char *alias, const char *orig)
return r;
}
-void
-rb_encdb_set_unicode(int index)
-{
- rb_raw_encoding *enc = (rb_raw_encoding *)rb_enc_from_index(index);
- ASSUME(enc);
- enc->flags |= ONIGENC_FLAG_UNICODE;
-}
-
static void
rb_enc_init(struct enc_table *enc_table)
{
enc_table_expand(enc_table, ENCODING_COUNT + 1);
if (!enc_table->names) {
- enc_table->names = st_init_strcasetable();
+ enc_table->names = st_init_strcasetable_with_size(ENCODING_LIST_CAPA);
}
#define OnigEncodingASCII_8BIT OnigEncodingASCII
#define ENC_REGISTER(enc) enc_register_at(enc_table, ENCINDEX_##enc, rb_enc_name(&OnigEncoding##enc), &OnigEncoding##enc)
@@ -882,11 +836,9 @@ rb_enc_autoload(rb_encoding *enc)
int
rb_enc_find_index(const char *name)
{
- int i;
+ int i = enc_registered(&global_enc_table, name);
rb_encoding *enc;
- GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_registered(enc_table, name));
-
if (i < 0) {
i = load_encoding(name);
}
@@ -1373,10 +1325,7 @@ enc_names(VALUE self)
args[0] = (VALUE)rb_to_encoding_index(self);
args[1] = rb_ary_new2(0);
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- st_foreach(enc_table->names, enc_names_i, (st_data_t)args));
-
+ st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args);
return args[1];
}
@@ -1402,14 +1351,7 @@ static VALUE
enc_list(VALUE klass)
{
VALUE ary = rb_ary_new2(0);
-
- RB_VM_LOCK_ENTER();
- {
- rb_ary_replace(ary, rb_default_encoding_list);
- rb_ary_concat(ary, rb_additional_encoding_list);
- }
- RB_VM_LOCK_LEAVE();
-
+ rb_ary_replace(ary, rb_encoding_list);
return ary;
}
@@ -1558,15 +1500,17 @@ rb_locale_encindex(void)
if (idx < 0) idx = ENCINDEX_UTF_8;
- GLOBAL_ENC_TABLE_ENTER(enc_table);
- if (enc_registered(enc_table, "locale") < 0) {
+ if (enc_registered(&global_enc_table, "locale") < 0) {
# if defined _WIN32
void Init_w32_codepage(void);
Init_w32_codepage();
# endif
- enc_alias_internal(enc_table, "locale", idx);
+ GLOBAL_ENC_TABLE_ENTER(enc_table);
+ {
+ enc_alias_internal(enc_table, "locale", idx);
+ }
+ GLOBAL_ENC_TABLE_LEAVE();
}
- GLOBAL_ENC_TABLE_LEAVE();
return idx;
}
@@ -1580,13 +1524,8 @@ rb_locale_encoding(void)
int
rb_filesystem_encindex(void)
{
- int idx;
-
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- idx = enc_registered(enc_table, "filesystem"));
-
- if (idx < 0)
- idx = ENCINDEX_ASCII_8BIT;
+ int idx = enc_registered(&global_enc_table, "filesystem");
+ if (idx < 0) idx = ENCINDEX_ASCII_8BIT;
return idx;
}
@@ -1617,7 +1556,14 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
if (NIL_P(encoding)) {
def->index = -1;
def->enc = 0;
- st_insert(enc_table->names, (st_data_t)strdup(name),
+ char *name_dup = strdup(name);
+
+ st_data_t existing_name = (st_data_t)name_dup;
+ if (st_delete(enc_table->names, &existing_name, NULL)) {
+ xfree((void *)existing_name);
+ }
+
+ st_insert(enc_table->names, (st_data_t)name_dup,
(st_data_t)UNSPECIFIED_ENCODING);
}
else {
@@ -1877,15 +1823,8 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg)
static VALUE
rb_enc_name_list(VALUE klass)
{
- VALUE ary;
-
- GLOBAL_ENC_TABLE_ENTER(enc_table);
- {
- ary = rb_ary_new2(enc_table->names->num_entries);
- st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary);
- }
- GLOBAL_ENC_TABLE_LEAVE();
-
+ VALUE ary = rb_ary_new2(global_enc_table.names->num_entries);
+ st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary);
return ary;
}
@@ -1931,8 +1870,7 @@ rb_enc_aliases(VALUE klass)
aliases[0] = rb_hash_new();
aliases[1] = rb_ary_new();
- GLOBAL_ENC_TABLE_EVAL(enc_table,
- st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases));
+ st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases);
return aliases[0];
}
@@ -2001,13 +1939,7 @@ Init_Encoding(void)
struct enc_table *enc_table = &global_enc_table;
- if (DEFAULT_ENCODING_LIST_CAPA < enc_table->count) rb_bug("DEFAULT_ENCODING_LIST_CAPA is too small");
-
- list = rb_additional_encoding_list = rb_ary_new();
- RBASIC_CLEAR_CLASS(list);
- rb_gc_register_mark_object(list);
-
- list = rb_default_encoding_list = rb_ary_new2(DEFAULT_ENCODING_LIST_CAPA);
+ list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA);
RBASIC_CLEAR_CLASS(list);
rb_gc_register_mark_object(list);
@@ -2029,5 +1961,5 @@ Init_encodings(void)
void
rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
{
- GLOBAL_ENC_TABLE_EVAL(enc_table, st_foreach(enc_table->names, func, arg));
+ st_foreach(global_enc_table.names, func, arg);
}
diff --git a/enum.c b/enum.c
index 97215b627e..b3c715e0a1 100644
--- a/enum.c
+++ b/enum.c
@@ -779,7 +779,7 @@ inject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
ENUM_WANT_SVALUE();
- if (memo->v1 == Qundef) {
+ if (UNDEF_P(memo->v1)) {
MEMO_V1_SET(memo, i);
}
else {
@@ -796,7 +796,7 @@ inject_op_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
ENUM_WANT_SVALUE();
- if (memo->v1 == Qundef) {
+ if (UNDEF_P(memo->v1)) {
MEMO_V1_SET(memo, i);
}
else if (SYMBOL_P(name = memo->u3.value)) {
@@ -820,9 +820,9 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
long i, n;
if (RARRAY_LEN(ary) == 0)
- return init == Qundef ? Qnil : init;
+ return UNDEF_P(init) ? Qnil : init;
- if (init == Qundef) {
+ if (UNDEF_P(init)) {
v = RARRAY_AREF(ary, 0);
i = 1;
if (RARRAY_LEN(ary) == 1)
@@ -1051,7 +1051,7 @@ enum_inject(int argc, VALUE *argv, VALUE obj)
memo = MEMO_NEW(init, Qnil, op);
rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
- if (memo->v1 == Qundef) return Qnil;
+ if (UNDEF_P(memo->v1)) return Qnil;
return memo->v1;
}
@@ -1373,7 +1373,6 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
static int
sort_by_cmp(const void *ap, const void *bp, void *data)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE a;
VALUE b;
VALUE ary = (VALUE)data;
@@ -1385,7 +1384,7 @@ sort_by_cmp(const void *ap, const void *bp, void *data)
a = *(VALUE *)ap;
b = *(VALUE *)bp;
- return OPTIMIZED_CMP(a, b, cmp_opt);
+ return OPTIMIZED_CMP(a, b);
}
/*
@@ -1677,7 +1676,7 @@ enum_any(int argc, VALUE *argv, VALUE obj)
DEFINE_ENUMFUNCS(one)
{
if (RTEST(result)) {
- if (memo->v1 == Qundef) {
+ if (UNDEF_P(memo->v1)) {
MEMO_V1_SET(memo, Qtrue);
}
else if (memo->v1 == Qtrue) {
@@ -1713,11 +1712,10 @@ cmpint_reenter_check(struct nmin_data *data, VALUE val)
static int
nmin_cmp(const void *ap, const void *bp, void *_data)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
#define rb_cmpint(cmp, a, b) rb_cmpint(cmpint_reenter_check(data, (cmp)), a, b)
- return OPTIMIZED_CMP(a, b, cmp_opt);
+ return OPTIMIZED_CMP(a, b);
#undef rb_cmpint
}
@@ -1827,7 +1825,7 @@ nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
else
cmpv = i;
- if (data->limit != Qundef) {
+ if (!UNDEF_P(data->limit)) {
int c = data->cmpfunc(&cmpv, &data->limit, data);
if (data->rev)
c = -c;
@@ -1962,7 +1960,7 @@ enum_one(int argc, VALUE *argv, VALUE obj)
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
result = memo->v1;
- if (result == Qundef) return Qfalse;
+ if (UNDEF_P(result)) return Qfalse;
return result;
}
@@ -2027,7 +2025,6 @@ enum_none(int argc, VALUE *argv, VALUE obj)
struct min_t {
VALUE min;
- struct cmp_opt_data cmp_opt;
};
static VALUE
@@ -2037,11 +2034,11 @@ min_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->min == Qundef) {
+ if (UNDEF_P(memo->min)) {
memo->min = i;
}
else {
- if (OPTIMIZED_CMP(i, memo->min, memo->cmp_opt) < 0) {
+ if (OPTIMIZED_CMP(i, memo->min) < 0) {
memo->min = i;
}
}
@@ -2056,7 +2053,7 @@ min_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->min == Qundef) {
+ if (UNDEF_P(memo->min)) {
memo->min = i;
}
else {
@@ -2130,7 +2127,7 @@ static VALUE
enum_min(int argc, VALUE *argv, VALUE obj)
{
VALUE memo;
- struct min_t *m = NEW_CMP_OPT_MEMO(struct min_t, memo);
+ struct min_t *m = NEW_MEMO_FOR(struct min_t, memo);
VALUE result;
VALUE num;
@@ -2138,8 +2135,6 @@ enum_min(int argc, VALUE *argv, VALUE obj)
return rb_nmin_run(obj, num, 0, 0, 0);
m->min = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, min_ii, memo);
}
@@ -2147,13 +2142,12 @@ enum_min(int argc, VALUE *argv, VALUE obj)
rb_block_call(obj, id_each, 0, 0, min_i, memo);
}
result = m->min;
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
struct max_t {
VALUE max;
- struct cmp_opt_data cmp_opt;
};
static VALUE
@@ -2163,11 +2157,11 @@ max_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->max == Qundef) {
+ if (UNDEF_P(memo->max)) {
memo->max = i;
}
else {
- if (OPTIMIZED_CMP(i, memo->max, memo->cmp_opt) > 0) {
+ if (OPTIMIZED_CMP(i, memo->max) > 0) {
memo->max = i;
}
}
@@ -2182,7 +2176,7 @@ max_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
ENUM_WANT_SVALUE();
- if (memo->max == Qundef) {
+ if (UNDEF_P(memo->max)) {
memo->max = i;
}
else {
@@ -2255,7 +2249,7 @@ static VALUE
enum_max(int argc, VALUE *argv, VALUE obj)
{
VALUE memo;
- struct max_t *m = NEW_CMP_OPT_MEMO(struct max_t, memo);
+ struct max_t *m = NEW_MEMO_FOR(struct max_t, memo);
VALUE result;
VALUE num;
@@ -2263,8 +2257,6 @@ enum_max(int argc, VALUE *argv, VALUE obj)
return rb_nmin_run(obj, num, 0, 1, 0);
m->max = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
}
@@ -2272,7 +2264,7 @@ enum_max(int argc, VALUE *argv, VALUE obj)
rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
}
result = m->max;
- if (result == Qundef) return Qnil;
+ if (UNDEF_P(result)) return Qnil;
return result;
}
@@ -2280,7 +2272,6 @@ struct minmax_t {
VALUE min;
VALUE max;
VALUE last;
- struct cmp_opt_data cmp_opt;
};
static void
@@ -2288,16 +2279,16 @@ minmax_i_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
- if (memo->min == Qundef) {
+ if (UNDEF_P(memo->min)) {
memo->min = i;
memo->max = j;
}
else {
- n = OPTIMIZED_CMP(i, memo->min, memo->cmp_opt);
+ n = OPTIMIZED_CMP(i, memo->min);
if (n < 0) {
memo->min = i;
}
- n = OPTIMIZED_CMP(j, memo->max, memo->cmp_opt);
+ n = OPTIMIZED_CMP(j, memo->max);
if (n > 0) {
memo->max = j;
}
@@ -2313,14 +2304,14 @@ minmax_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->last == Qundef) {
+ if (UNDEF_P(memo->last)) {
memo->last = i;
return Qnil;
}
j = memo->last;
memo->last = Qundef;
- n = OPTIMIZED_CMP(j, i, memo->cmp_opt);
+ n = OPTIMIZED_CMP(j, i);
if (n == 0)
i = j;
else if (n < 0) {
@@ -2340,7 +2331,7 @@ minmax_ii_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
- if (memo->min == Qundef) {
+ if (UNDEF_P(memo->min)) {
memo->min = i;
memo->max = j;
}
@@ -2365,7 +2356,7 @@ minmax_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->last == Qundef) {
+ if (UNDEF_P(memo->last)) {
memo->last = i;
return Qnil;
}
@@ -2422,23 +2413,21 @@ static VALUE
enum_minmax(VALUE obj)
{
VALUE memo;
- struct minmax_t *m = NEW_CMP_OPT_MEMO(struct minmax_t, memo);
+ struct minmax_t *m = NEW_MEMO_FOR(struct minmax_t, memo);
m->min = Qundef;
m->last = Qundef;
- m->cmp_opt.opt_methods = 0;
- m->cmp_opt.opt_inited = 0;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, minmax_ii, memo);
- if (m->last != Qundef)
+ if (!UNDEF_P(m->last))
minmax_ii_update(m->last, m->last, m);
}
else {
rb_block_call(obj, id_each, 0, 0, minmax_i, memo);
- if (m->last != Qundef)
+ if (!UNDEF_P(m->last))
minmax_i_update(m->last, m->last, m);
}
- if (m->min != Qundef) {
+ if (!UNDEF_P(m->min)) {
return rb_assoc_new(m->min, m->max);
}
return rb_assoc_new(Qnil, Qnil);
@@ -2447,18 +2436,17 @@ enum_minmax(VALUE obj)
static VALUE
min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args);
VALUE v;
ENUM_WANT_SVALUE();
v = enum_yield(argc, i);
- if (memo->v1 == Qundef) {
+ if (UNDEF_P(memo->v1)) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
- else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) < 0) {
+ else if (OPTIMIZED_CMP(v, memo->v1) < 0) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
@@ -2522,18 +2510,17 @@ enum_min_by(int argc, VALUE *argv, VALUE obj)
static VALUE
max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args);
VALUE v;
ENUM_WANT_SVALUE();
v = enum_yield(argc, i);
- if (memo->v1 == Qundef) {
+ if (UNDEF_P(memo->v1)) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
- else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) > 0) {
+ else if (OPTIMIZED_CMP(v, memo->v1) > 0) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
@@ -2606,20 +2593,18 @@ struct minmax_by_t {
static void
minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *memo)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
-
- if (memo->min_bv == Qundef) {
+ if (UNDEF_P(memo->min_bv)) {
memo->min_bv = v1;
memo->max_bv = v2;
memo->min = i1;
memo->max = i2;
}
else {
- if (OPTIMIZED_CMP(v1, memo->min_bv, cmp_opt) < 0) {
+ if (OPTIMIZED_CMP(v1, memo->min_bv) < 0) {
memo->min_bv = v1;
memo->min = i1;
}
- if (OPTIMIZED_CMP(v2, memo->max_bv, cmp_opt) > 0) {
+ if (OPTIMIZED_CMP(v2, memo->max_bv) > 0) {
memo->max_bv = v2;
memo->max = i2;
}
@@ -2629,7 +2614,6 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m
static VALUE
minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
struct minmax_by_t *memo = MEMO_FOR(struct minmax_by_t, _memo);
VALUE vi, vj, j;
int n;
@@ -2638,7 +2622,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
vi = enum_yield(argc, i);
- if (memo->last_bv == Qundef) {
+ if (UNDEF_P(memo->last_bv)) {
memo->last_bv = vi;
memo->last = i;
return Qnil;
@@ -2647,7 +2631,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
j = memo->last;
memo->last_bv = Qundef;
- n = OPTIMIZED_CMP(vj, vi, cmp_opt);
+ n = OPTIMIZED_CMP(vj, vi);
if (n == 0) {
i = j;
vi = vj;
@@ -2705,7 +2689,7 @@ enum_minmax_by(VALUE obj)
m->last_bv = Qundef;
m->last = Qundef;
rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo);
- if (m->last_bv != Qundef)
+ if (!UNDEF_P(m->last_bv))
minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m);
m = MEMO_FOR(struct minmax_by_t, memo);
return rb_assoc_new(m->min, m->max);
@@ -3033,7 +3017,6 @@ each_cons_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
static VALUE
enum_each_cons_size(VALUE obj, VALUE args, VALUE eobj)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
const VALUE zero = LONG2FIX(0);
VALUE n, size;
long cons_size = NUM2LONG(RARRAY_AREF(args, 0));
@@ -3043,7 +3026,7 @@ enum_each_cons_size(VALUE obj, VALUE args, VALUE eobj)
if (NIL_P(size)) return Qnil;
n = add_int(size, 1 - cons_size);
- return (OPTIMIZED_CMP(n, zero, cmp_opt) == -1) ? zero : n;
+ return (OPTIMIZED_CMP(n, zero) == -1) ? zero : n;
}
/*
@@ -3185,7 +3168,7 @@ zip_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval))
v[1] = RARRAY_AREF(args, i);
rb_rescue2(call_next, (VALUE)v, call_stop, (VALUE)v, rb_eStopIteration, (VALUE)0);
- if (v[0] == Qundef) {
+ if (UNDEF_P(v[0])) {
RARRAY_ASET(args, i, Qnil);
v[0] = Qnil;
}
@@ -3834,7 +3817,7 @@ slicebefore_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
/*
* call-seq:
* slice_before(pattern) -> enumerator
- * slice_before {|array| ... } -> enumerator
+ * slice_before {|elt| ... } -> enumerator
*
* With argument +pattern+, returns an enumerator that uses the pattern
* to partition elements into arrays ("slices").
@@ -4155,7 +4138,7 @@ slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
ENUM_WANT_SVALUE();
- if (memo->prev_elt == Qundef) {
+ if (UNDEF_P(memo->prev_elt)) {
/* The first element */
memo->prev_elt = i;
memo->prev_elts = rb_ary_new3(1, i);
@@ -4395,7 +4378,7 @@ sum_iter_bignum(VALUE i, struct enum_sum_memo *memo)
static void
sum_iter_rational(VALUE i, struct enum_sum_memo *memo)
{
- if (memo->r == Qundef) {
+ if (UNDEF_P(memo->r)) {
memo->r = i;
}
else {
@@ -4476,7 +4459,7 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
}
else switch (TYPE(memo->v)) {
default: sum_iter_some_value(i, memo); return;
- case T_FLOAT: sum_iter_Kahan_Babuska(i, memo); return;
+ case T_FLOAT:
case T_FIXNUM:
case T_BIGNUM:
case T_RATIONAL:
@@ -4616,7 +4599,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
else {
if (memo.n != 0)
memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v);
- if (memo.r != Qundef) {
+ if (!UNDEF_P(memo.r)) {
memo.v = rb_rational_plus(memo.r, memo.v);
}
return memo.v;
diff --git a/enumerator.c b/enumerator.c
index 2c9858cda6..d587b63d32 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -20,6 +20,7 @@
#include "id.h"
#include "internal.h"
+#include "internal/class.h"
#include "internal/enumerator.h"
#include "internal/error.h"
#include "internal/hash.h"
@@ -72,6 +73,8 @@
* puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
* # => ["0:foo", "1:bar", "2:baz"]
*
+ * == External Iteration
+ *
* An Enumerator can also be used as an external iterator.
* For example, Enumerator#next returns the next value of the iterator
* or raises StopIteration if the Enumerator is at the end.
@@ -82,15 +85,44 @@
* puts e.next # => 3
* puts e.next # raises StopIteration
*
- * Note that enumeration sequence by +next+, +next_values+, +peek+ and
- * +peek_values+ do not affect other non-external
- * enumeration methods, unless the underlying iteration method itself has
- * side-effect, e.g. IO#each_line.
+ * +next+, +next_values+, +peek+ and +peek_values+ are the only methods
+ * which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+).
+ *
+ * These methods do not affect other internal enumeration methods,
+ * unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
+ *
+ * External iteration differs *significantly* from internal iteration
+ * due to using a Fiber:
+ * - The Fiber adds some overhead compared to internal enumeration.
+ * - The stacktrace will only include the stack from the Enumerator, not above.
+ * - Fiber-local variables are *not* inherited inside the Enumerator Fiber,
+ * which instead starts with no Fiber-local variables.
+ * - Fiber storage variables *are* inherited and are designed
+ * to handle Enumerator Fibers. Assigning to a Fiber storage variable
+ * only affects the current Fiber, so if you want to change state
+ * in the caller Fiber of the Enumerator Fiber, you need to use an
+ * extra indirection (e.g., use some object in the Fiber storage
+ * variable and mutate some ivar of it).
+ *
+ * Concretely:
+ * Thread.current[:fiber_local] = 1
+ * Fiber[:storage_var] = 1
+ * e = Enumerator.new do |y|
+ * p Thread.current[:fiber_local] # for external iteration: nil, for internal iteration: 1
+ * p Fiber[:storage_var] # => 1, inherited
+ * Fiber[:storage_var] += 1
+ * y << 42
+ * end
+ *
+ * p e.next # => 42
+ * p Fiber[:storage_var] # => 1 (it ran in a different Fiber)
*
- * Moreover, implementation typically uses fibers so performance could be
- * slower and exception stacktraces different than expected.
+ * e.each { p _1 }
+ * p Fiber[:storage_var] # => 2 (it ran in the same Fiber/"stack" as the current Fiber)
*
- * You can use this to implement an internal iterator as follows:
+ * == Convert External Iteration to Internal Iteration
+ *
+ * You can use an external iterator to implement an internal iterator as follows:
*
* def ext_each(e)
* while true
@@ -132,7 +164,10 @@ static VALUE sym_each, sym_cycle, sym_yield;
static VALUE lazy_use_super_method;
+extern ID ruby_static_id_cause;
+
#define id_call idCall
+#define id_cause ruby_static_id_cause
#define id_each idEach
#define id_eqq idEqq
#define id_initialize idInitialize
@@ -173,9 +208,11 @@ struct producer {
typedef struct MEMO *lazyenum_proc_func(VALUE, struct MEMO *, VALUE, long);
typedef VALUE lazyenum_size_func(VALUE, VALUE);
+typedef int lazyenum_precheck_func(VALUE proc_entry);
typedef struct {
lazyenum_proc_func *proc;
lazyenum_size_func *size;
+ lazyenum_precheck_func *precheck;
} lazyenum_funcs;
struct proc_entry {
@@ -260,7 +297,7 @@ enumerator_ptr(VALUE obj)
struct enumerator *ptr;
TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr);
- if (!ptr || ptr->obj == Qundef) {
+ if (!ptr || UNDEF_P(ptr->obj)) {
rb_raise(rb_eArgError, "uninitialized enumerator");
}
return ptr;
@@ -519,8 +556,8 @@ rb_enumeratorize(VALUE obj, VALUE meth, int argc, const VALUE *argv)
return rb_enumeratorize_with_size(obj, meth, argc, argv, 0);
}
-static VALUE
-lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat);
+static VALUE lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat);
+static int lazy_precheck(VALUE procs);
VALUE
rb_enumeratorize_with_size_kw(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat)
@@ -598,9 +635,10 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
static VALUE
enumerator_each(int argc, VALUE *argv, VALUE obj)
{
+ struct enumerator *e = enumerator_ptr(obj);
+
if (argc > 0) {
- struct enumerator *e = enumerator_ptr(obj = rb_obj_dup(obj));
- VALUE args = e->args;
+ VALUE args = (e = enumerator_ptr(obj = rb_obj_dup(obj)))->args;
if (args) {
#if SIZEOF_INT < SIZEOF_LONG
/* check int range overflow */
@@ -617,6 +655,9 @@ enumerator_each(int argc, VALUE *argv, VALUE obj)
e->size_fn = 0;
}
if (!rb_block_given_p()) return obj;
+
+ if (!lazy_precheck(e->procs)) return Qnil;
+
return enumerator_block_call(obj, 0, obj);
}
@@ -735,7 +776,7 @@ next_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, obj))
VALUE feedvalue = Qnil;
VALUE args = rb_ary_new4(argc, argv);
rb_fiber_yield(1, &args);
- if (e->feedvalue != Qundef) {
+ if (!UNDEF_P(e->feedvalue)) {
feedvalue = e->feedvalue;
e->feedvalue = Qundef;
}
@@ -769,8 +810,16 @@ get_next_values(VALUE obj, struct enumerator *e)
{
VALUE curr, vs;
- if (e->stop_exc)
- rb_exc_raise(e->stop_exc);
+ if (e->stop_exc) {
+ VALUE exc = e->stop_exc;
+ VALUE result = rb_attr_get(exc, id_result);
+ VALUE mesg = rb_attr_get(exc, idMesg);
+ if (!NIL_P(mesg)) mesg = rb_str_dup(mesg);
+ VALUE stop_exc = rb_exc_new_str(rb_eStopIteration, mesg);
+ rb_ivar_set(stop_exc, id_cause, exc);
+ rb_ivar_set(stop_exc, id_result, result);
+ rb_exc_raise(stop_exc);
+ }
curr = rb_fiber_current();
@@ -840,7 +889,7 @@ enumerator_next_values(VALUE obj)
struct enumerator *e = enumerator_ptr(obj);
VALUE vs;
- if (e->lookahead != Qundef) {
+ if (!UNDEF_P(e->lookahead)) {
vs = e->lookahead;
e->lookahead = Qundef;
return vs;
@@ -901,7 +950,7 @@ enumerator_peek_values(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
- if (e->lookahead == Qundef) {
+ if (UNDEF_P(e->lookahead)) {
e->lookahead = get_next_values(obj, e);
}
return e->lookahead;
@@ -1025,7 +1074,7 @@ enumerator_feed(VALUE obj, VALUE v)
{
struct enumerator *e = enumerator_ptr(obj);
- if (e->feedvalue != Qundef) {
+ if (!UNDEF_P(e->feedvalue)) {
rb_raise(rb_eTypeError, "feed value already set");
}
e->feedvalue = v;
@@ -1070,7 +1119,7 @@ inspect_enumerator(VALUE obj, VALUE dummy, int recur)
cname = rb_obj_class(obj);
- if (!e || e->obj == Qundef) {
+ if (!e || UNDEF_P(e->obj)) {
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(cname));
}
@@ -1239,7 +1288,7 @@ enumerator_size(VALUE obj)
argv = RARRAY_CONST_PTR(e->args);
}
size = rb_check_funcall_kw(e->size, id_call, argc, argv, e->kw_splat);
- if (size != Qundef) return size;
+ if (!UNDEF_P(size)) return size;
return e->size;
}
@@ -1285,7 +1334,7 @@ yielder_ptr(VALUE obj)
struct yielder *ptr;
TypedData_Get_Struct(obj, struct yielder, &yielder_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
+ if (!ptr || UNDEF_P(ptr->proc)) {
rb_raise(rb_eArgError, "uninitialized yielder");
}
return ptr;
@@ -1425,7 +1474,7 @@ generator_ptr(VALUE obj)
struct generator *ptr;
TypedData_Get_Struct(obj, struct generator, &generator_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
+ if (!ptr || UNDEF_P(ptr->proc)) {
rb_raise(rb_eArgError, "uninitialized generator");
}
return ptr;
@@ -1529,7 +1578,7 @@ static VALUE
enum_size(VALUE self)
{
VALUE r = rb_check_funcall(self, id_size, 0, 0);
- return (r == Qundef) ? Qnil : r;
+ return UNDEF_P(r) ? Qnil : r;
}
static VALUE
@@ -1562,7 +1611,7 @@ lazy_init_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
result = rb_yield_values2(len, nargv);
ALLOCV_END(args);
}
- if (result == Qundef) rb_iter_break();
+ if (UNDEF_P(result)) rb_iter_break();
return Qnil;
}
@@ -1676,6 +1725,22 @@ lazy_generator_init(VALUE enumerator, VALUE procs)
return generator;
}
+static int
+lazy_precheck(VALUE procs)
+{
+ if (RTEST(procs)) {
+ long num_procs = RARRAY_LEN(procs), i = num_procs;
+ while (i-- > 0) {
+ VALUE proc = RARRAY_AREF(procs, i);
+ struct proc_entry *entry = proc_entry_ptr(proc);
+ lazyenum_precheck_func *precheck = entry->fn->precheck;
+ if (precheck && !precheck(proc)) return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
/*
* Document-class: Enumerator::Lazy
*
@@ -2425,13 +2490,8 @@ lazy_take_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_ind
}
remain = NUM2LONG(memo);
- if (remain == 0) {
- LAZY_MEMO_SET_BREAK(result);
- }
- else {
- if (--remain == 0) LAZY_MEMO_SET_BREAK(result);
- rb_ary_store(memos, memo_index, LONG2NUM(remain));
- }
+ if (--remain == 0) LAZY_MEMO_SET_BREAK(result);
+ rb_ary_store(memos, memo_index, LONG2NUM(remain));
return result;
}
@@ -2444,8 +2504,15 @@ lazy_take_size(VALUE entry, VALUE receiver)
return LONG2NUM(len);
}
+static int
+lazy_take_precheck(VALUE proc_entry)
+{
+ struct proc_entry *entry = proc_entry_ptr(proc_entry);
+ return entry->memo != INT2FIX(0);
+}
+
static const lazyenum_funcs lazy_take_funcs = {
- lazy_take_proc, lazy_take_size,
+ lazy_take_proc, lazy_take_size, lazy_take_precheck,
};
/*
@@ -2459,20 +2526,14 @@ static VALUE
lazy_take(VALUE obj, VALUE n)
{
long len = NUM2LONG(n);
- int argc = 0;
- VALUE argv[2];
if (len < 0) {
rb_raise(rb_eArgError, "attempt to take negative size");
}
- if (len == 0) {
- argv[0] = sym_cycle;
- argv[1] = INT2NUM(0);
- argc = 2;
- }
+ n = LONG2NUM(len); /* no more conversion */
- return lazy_add_method(obj, argc, argv, n, rb_ary_new3(1, n), &lazy_take_funcs);
+ return lazy_add_method(obj, 0, 0, n, rb_ary_new3(1, n), &lazy_take_funcs);
}
static struct MEMO *
@@ -2923,7 +2984,7 @@ producer_ptr(VALUE obj)
struct producer *ptr;
TypedData_Get_Struct(obj, struct producer, &producer_data_type, ptr);
- if (!ptr || ptr->proc == Qundef) {
+ if (!ptr || UNDEF_P(ptr->proc)) {
rb_raise(rb_eArgError, "uninitialized producer");
}
return ptr;
@@ -2978,7 +3039,7 @@ producer_each_i(VALUE obj)
init = ptr->init;
proc = ptr->proc;
- if (init == Qundef) {
+ if (UNDEF_P(init)) {
curr = Qnil;
}
else {
@@ -3109,7 +3170,7 @@ enum_chain_ptr(VALUE obj)
struct enum_chain *ptr;
TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
rb_raise(rb_eArgError, "uninitialized chain");
}
return ptr;
@@ -3302,7 +3363,7 @@ inspect_enum_chain(VALUE obj, VALUE dummy, int recur)
TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass));
}
@@ -3384,7 +3445,7 @@ enumerator_plus(VALUE obj, VALUE eobj)
*
* The method used against each enumerable object is `each_entry`
* instead of `each` so that the product of N enumerable objects
- * yields exactly N arguments in each iteration.
+ * yields an array of exactly N elements in each iteration.
*
* When no enumerator is given, it calls a given block once yielding
* an empty argument list.
@@ -3431,7 +3492,7 @@ enum_product_ptr(VALUE obj)
struct enum_product *ptr;
TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
rb_raise(rb_eArgError, "uninitialized product");
}
return ptr;
@@ -3462,9 +3523,16 @@ enum_product_allocate(VALUE klass)
* e.size #=> 6
*/
static VALUE
-enum_product_initialize(VALUE obj, VALUE enums)
+enum_product_initialize(int argc, VALUE *argv, VALUE obj)
{
struct enum_product *ptr;
+ VALUE enums = Qnil, options = Qnil;
+
+ rb_scan_args(argc, argv, "*:", &enums, &options);
+
+ if (!NIL_P(options) && !RHASH_EMPTY_P(options)) {
+ rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options)));
+ }
rb_check_frozen(obj);
TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
@@ -3570,7 +3638,7 @@ product_each(VALUE obj, struct product_state *pstate)
rb_block_call(eobj, id_each_entry, 0, NULL, product_each_i, (VALUE)pstate);
}
else {
- rb_funcallv(pstate->block, id_call, pstate->argc, pstate->argv);
+ rb_funcall(pstate->block, id_call, 1, rb_ary_new_from_values(pstate->argc, pstate->argv));
}
return obj;
@@ -3642,7 +3710,7 @@ inspect_enum_product(VALUE obj, VALUE dummy, int recur)
TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr);
- if (!ptr || ptr->enums == Qundef) {
+ if (!ptr || UNDEF_P(ptr->enums)) {
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass));
}
@@ -3668,6 +3736,7 @@ enum_product_inspect(VALUE obj)
/*
* call-seq:
* Enumerator.product(*enums) -> enumerator
+ * Enumerator.product(*enums) { |elts| ... } -> enumerator
*
* Generates a new enumerator object that generates a Cartesian
* product of given enumerable objects. This is equivalent to
@@ -3676,18 +3745,29 @@ enum_product_inspect(VALUE obj)
* e = Enumerator.product(1..3, [4, 5])
* e.to_a #=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
* e.size #=> 6
+ *
+ * When a block is given, calls the block with each N-element array
+ * generated and returns +nil+.
*/
static VALUE
-enumerator_s_product(VALUE klass, VALUE enums)
+enumerator_s_product(int argc, VALUE *argv, VALUE klass)
{
- VALUE obj = enum_product_initialize(enum_product_allocate(rb_cEnumProduct), enums);
+ VALUE enums = Qnil, options = Qnil, block = Qnil;
- if (rb_block_given_p()) {
- return enum_product_run(obj, rb_block_proc());
+ rb_scan_args(argc, argv, "*:&", &enums, &options, &block);
+
+ if (!NIL_P(options) && !RHASH_EMPTY_P(options)) {
+ rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options)));
}
- else {
- return obj;
+
+ VALUE obj = enum_product_initialize(argc, argv, enum_product_allocate(rb_cEnumProduct));
+
+ if (!NIL_P(block)) {
+ enum_product_run(obj, block);
+ return Qnil;
}
+
+ return obj;
}
/*
@@ -4567,7 +4647,7 @@ InitVM_Enumerator(void)
/* Product */
rb_cEnumProduct = rb_define_class_under(rb_cEnumerator, "Product", rb_cEnumerator);
rb_define_alloc_func(rb_cEnumProduct, enum_product_allocate);
- rb_define_method(rb_cEnumProduct, "initialize", enum_product_initialize, -2);
+ rb_define_method(rb_cEnumProduct, "initialize", enum_product_initialize, -1);
rb_define_method(rb_cEnumProduct, "initialize_copy", enum_product_init_copy, 1);
rb_define_method(rb_cEnumProduct, "each", enum_product_each, 0);
rb_define_method(rb_cEnumProduct, "size", enum_product_size, 0);
@@ -4578,7 +4658,7 @@ InitVM_Enumerator(void)
rb_undef_method(rb_cEnumProduct, "next_values");
rb_undef_method(rb_cEnumProduct, "peek");
rb_undef_method(rb_cEnumProduct, "peek_values");
- rb_define_singleton_method(rb_cEnumerator, "product", enumerator_s_product, -2);
+ rb_define_singleton_method(rb_cEnumerator, "product", enumerator_s_product, -1);
/* ArithmeticSequence */
rb_cArithSeq = rb_define_class_under(rb_cEnumerator, "ArithmeticSequence", rb_cEnumerator);
diff --git a/error.c b/error.c
index 07fa04627e..726f57a4c0 100644
--- a/error.c
+++ b/error.c
@@ -34,6 +34,7 @@
#include "internal/io.h"
#include "internal/load.h"
#include "internal/object.h"
+#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
@@ -124,6 +125,8 @@ err_vcatf(VALUE str, const char *pre, const char *file, int line,
return str;
}
+static VALUE syntax_error_with_path(VALUE, VALUE, VALUE*, rb_encoding*);
+
VALUE
rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
rb_encoding *enc, const char *fmt, va_list args)
@@ -137,15 +140,7 @@ rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
}
else {
VALUE mesg;
- if (NIL_P(exc)) {
- mesg = rb_enc_str_new(0, 0, enc);
- exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError);
- }
- else {
- mesg = rb_attr_get(exc, idMesg);
- if (RSTRING_LEN(mesg) > 0 && *(RSTRING_END(mesg)-1) != '\n')
- rb_str_cat_cstr(mesg, "\n");
- }
+ exc = syntax_error_with_path(exc, file, &mesg, enc);
err_vcatf(mesg, NULL, fn, line, fmt, args);
}
@@ -405,8 +400,10 @@ warning_string(rb_encoding *enc, const char *fmt, va_list args)
}
#define with_warning_string(mesg, enc, fmt) \
+ with_warning_string_from(mesg, enc, fmt, fmt)
+#define with_warning_string_from(mesg, enc, fmt, last_arg) \
VALUE mesg; \
- va_list args; va_start(args, fmt); \
+ va_list args; va_start(args, last_arg); \
mesg = warning_string(enc, fmt, args); \
va_end(args);
@@ -423,7 +420,7 @@ rb_warn(const char *fmt, ...)
void
rb_category_warn(rb_warning_category_t category, const char *fmt, ...)
{
- if (!NIL_P(ruby_verbose)) {
+ if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -455,7 +452,7 @@ rb_warning(const char *fmt, ...)
void
rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
{
- if (RTEST(ruby_verbose)) {
+ if (RTEST(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -508,12 +505,9 @@ rb_warn_deprecated(const char *fmt, const char *suggest, ...)
{
if (!deprecation_warning_enabled()) return;
- va_list args;
- va_start(args, suggest);
- VALUE mesg = warning_string(0, fmt, args);
- va_end(args);
-
- warn_deprecated(mesg, NULL, suggest);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, NULL, suggest);
+ }
}
void
@@ -521,12 +515,9 @@ rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *s
{
if (!deprecation_warning_enabled()) return;
- va_list args;
- va_start(args, suggest);
- VALUE mesg = warning_string(0, fmt, args);
- va_end(args);
-
- warn_deprecated(mesg, removal, suggest);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, removal, suggest);
+ }
}
static inline int
@@ -669,6 +660,11 @@ bug_important_message(FILE *out, const char *const msg, size_t len)
fwrite(p, 1, endmsg - p, out);
}
+#undef CRASH_REPORTER_MAY_BE_CREATED
+#if defined(__APPLE__) && \
+ (!defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 || defined(__POWERPC__)) /* 10.6 PPC case */
+# define CRASH_REPORTER_MAY_BE_CREATED
+#endif
static void
preface_dump(FILE *out)
{
@@ -677,7 +673,7 @@ preface_dump(FILE *out)
"-- Crash Report log information "
"--------------------------------------------\n"
" See Crash Report log file in one of the following locations:\n"
-# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
" * ~/Library/Logs/CrashReporter\n"
" * /Library/Logs/CrashReporter\n"
# endif
@@ -702,7 +698,7 @@ postscript_dump(FILE *out)
"[IMPORTANT]"
/*" ------------------------------------------------"*/
"\n""Don't forget to include the Crash Report log file under\n"
-# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
"CrashReporter or "
# endif
"DiagnosticReports directory in bug reports.\n"
@@ -1003,7 +999,7 @@ rb_check_type(VALUE x, int t)
{
int xt;
- if (RB_UNLIKELY(x == Qundef)) {
+ if (RB_UNLIKELY(UNDEF_P(x))) {
rb_bug(UNDEF_LEAKED);
}
@@ -1024,7 +1020,7 @@ rb_check_type(VALUE x, int t)
void
rb_unexpected_type(VALUE x, int t)
{
- if (RB_UNLIKELY(x == Qundef)) {
+ if (RB_UNLIKELY(UNDEF_P(x))) {
rb_bug(UNDEF_LEAKED);
}
@@ -1226,7 +1222,7 @@ VALUE
rb_get_message(VALUE exc)
{
VALUE e = rb_check_funcall(exc, id_message, 0, 0);
- if (e == Qundef) return Qnil;
+ if (UNDEF_P(e)) return Qnil;
if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
return e;
}
@@ -1241,7 +1237,7 @@ rb_get_detailed_message(VALUE exc, VALUE opt)
else {
e = rb_check_funcall_kw(exc, id_detailed_message, 1, &opt, 1);
}
- if (e == Qundef) return Qnil;
+ if (UNDEF_P(e)) return Qnil;
if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
return e;
}
@@ -1439,8 +1435,15 @@ exc_inspect(VALUE exc)
str = rb_str_buf_new2("#<");
klass = rb_class_name(klass);
rb_str_buf_append(str, klass);
- rb_str_buf_cat(str, ": ", 2);
- rb_str_buf_append(str, exc);
+
+ if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
+ rb_str_catf(str, ":%+"PRIsVALUE, exc);
+ }
+ else {
+ rb_str_buf_cat(str, ": ", 2);
+ rb_str_buf_append(str, exc);
+ }
+
rb_str_buf_cat(str, ">", 1);
return str;
@@ -1626,15 +1629,15 @@ exc_equal(VALUE exc, VALUE obj)
int state;
obj = rb_protect(try_convert_to_exception, obj, &state);
- if (state || obj == Qundef) {
+ if (state || UNDEF_P(obj)) {
rb_set_errinfo(Qnil);
return Qfalse;
}
if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
mesg = rb_check_funcall(obj, id_message, 0, 0);
- if (mesg == Qundef) return Qfalse;
+ if (UNDEF_P(mesg)) return Qfalse;
backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
- if (backtrace == Qundef) return Qfalse;
+ if (UNDEF_P(backtrace)) return Qfalse;
}
else {
mesg = rb_attr_get(obj, id_mesg);
@@ -1737,7 +1740,7 @@ exit_success_p(VALUE exc)
static VALUE
err_init_recv(VALUE exc, VALUE recv)
{
- if (recv != Qundef) rb_ivar_set(exc, id_recv, recv);
+ if (!UNDEF_P(recv)) rb_ivar_set(exc, id_recv, recv);
return exc;
}
@@ -1815,7 +1818,9 @@ name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
rb_ivar_set(exc, id_name, method);
err_init_recv(exc, recv);
- if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ if (cfp && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) {
+ rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ }
return exc;
}
@@ -2081,7 +2086,7 @@ name_err_mesg_to_str(VALUE obj)
break;
default:
d = rb_protect(name_err_mesg_receiver_name, obj, &state);
- if (state || d == Qundef || NIL_P(d))
+ if (state || NIL_OR_UNDEF_P(d))
d = rb_protect(rb_inspect, obj, &state);
if (state) {
rb_set_errinfo(Qnil);
@@ -2136,7 +2141,7 @@ name_err_receiver(VALUE self)
VALUE *ptr, recv, mesg;
recv = rb_ivar_lookup(self, id_recv, Qundef);
- if (recv != Qundef) return recv;
+ if (!UNDEF_P(recv)) return recv;
mesg = rb_attr_get(self, id_mesg);
if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) {
@@ -2194,7 +2199,7 @@ key_err_receiver(VALUE self)
VALUE recv;
recv = rb_ivar_lookup(self, id_receiver, Qundef);
- if (recv != Qundef) return recv;
+ if (!UNDEF_P(recv)) return recv;
rb_raise(rb_eArgError, "no receiver is available");
}
@@ -2211,7 +2216,7 @@ key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2249,7 +2254,7 @@ key_err_initialize(int argc, VALUE *argv, VALUE self)
keywords[1] = id_key;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
for (i = 0; i < numberof(values); ++i) {
- if (values[i] != Qundef) {
+ if (!UNDEF_P(values[i])) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
@@ -2271,7 +2276,7 @@ no_matching_pattern_key_err_matchee(VALUE self)
VALUE matchee;
matchee = rb_ivar_lookup(self, id_matchee, Qundef);
- if (matchee != Qundef) return matchee;
+ if (!UNDEF_P(matchee)) return matchee;
rb_raise(rb_eArgError, "no matchee is available");
}
@@ -2288,7 +2293,7 @@ no_matching_pattern_key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2315,7 +2320,7 @@ no_matching_pattern_key_err_initialize(int argc, VALUE *argv, VALUE self)
keywords[1] = id_key;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
for (i = 0; i < numberof(values); ++i) {
- if (values[i] != Qundef) {
+ if (!UNDEF_P(values[i])) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
@@ -2344,6 +2349,25 @@ syntax_error_initialize(int argc, VALUE *argv, VALUE self)
return rb_call_super(argc, argv);
}
+static VALUE
+syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
+{
+ if (NIL_P(exc)) {
+ *mesg = rb_enc_str_new(0, 0, enc);
+ exc = rb_class_new_instance(1, mesg, rb_eSyntaxError);
+ rb_ivar_set(exc, id_i_path, path);
+ }
+ else {
+ if (rb_attr_get(exc, id_i_path) != path) {
+ rb_raise(rb_eArgError, "SyntaxError#path changed");
+ }
+ VALUE s = *mesg = rb_attr_get(exc, idMesg);
+ if (RSTRING_LEN(s) > 0 && *(RSTRING_END(s)-1) != '\n')
+ rb_str_cat_cstr(s, "\n");
+ }
+ return exc;
+}
+
/*
* Document-module: Errno
*
@@ -2437,9 +2461,6 @@ get_syserr(int n)
static VALUE
syserr_initialize(int argc, VALUE *argv, VALUE self)
{
-#if !defined(_WIN32)
- char *strerror();
-#endif
const char *err;
VALUE mesg, error, func, errmsg;
VALUE klass = rb_obj_class(self);
@@ -2940,6 +2961,8 @@ ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc)
return ST_CONTINUE;
}
+void rb_exc_check_circular_cause(VALUE exc);
+
static VALUE
exception_loader(VALUE exc, VALUE obj)
{
@@ -2954,6 +2977,8 @@ exception_loader(VALUE exc, VALUE obj)
rb_ivar_foreach(obj, ivar_copy_i, exc);
+ rb_exc_check_circular_cause(exc);
+
if (rb_attr_get(exc, id_bt) == rb_attr_get(exc, id_bt_locations)) {
rb_ivar_set(exc, id_bt_locations, Qnil);
}
@@ -3005,9 +3030,16 @@ Init_Exception(void)
rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1);
+ /* RDoc will use literal name value while parsing rb_attr,
+ * and will render `idPath` as an attribute name without this trick */
+ ID path = idPath;
+
+ /* the path failed to parse */
+ rb_attr(rb_eSyntaxError, path, TRUE, FALSE, FALSE);
+
rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
/* the path failed to load */
- rb_attr(rb_eLoadError, rb_intern_const("path"), TRUE, FALSE, FALSE);
+ rb_attr(rb_eLoadError, path, TRUE, FALSE, FALSE);
rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
diff --git a/eval.c b/eval.c
index cf32a82214..a61dfb1289 100644
--- a/eval.c
+++ b/eval.c
@@ -21,6 +21,7 @@
#include "gc.h"
#include "internal.h"
#include "internal/class.h"
+#include "internal/cont.h"
#include "internal/error.h"
#include "internal/eval.h"
#include "internal/hash.h"
@@ -42,7 +43,7 @@ NORETURN(static void rb_raise_jump(VALUE, VALUE));
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
void rb_ec_clear_all_trace_func(const rb_execution_context_t *ec);
-static int rb_ec_cleanup(rb_execution_context_t *ec, int ex);
+static int rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex);
static int rb_ec_exec_node(rb_execution_context_t *ec, void *n);
VALUE rb_eLocalJumpError;
@@ -99,8 +100,10 @@ ruby_init(void)
{
int state = ruby_setup();
if (state) {
- if (RTEST(ruby_debug))
- error_print(GET_EC());
+ if (RTEST(ruby_debug)) {
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_error_print(ec, ec->errinfo);
+ }
exit(EXIT_FAILURE);
}
}
@@ -119,8 +122,9 @@ ruby_options(int argc, char **argv)
}
else {
rb_ec_clear_current_thread_trace_func(ec);
- state = error_handle(ec, state);
- iseq = (void *)INT2FIX(state);
+ int exitcode = error_handle(ec, ec->errinfo, state);
+ ec->errinfo = Qnil; /* just been handled */
+ iseq = (void *)INT2FIX(exitcode);
}
EC_POP_TAG();
return iseq;
@@ -136,7 +140,7 @@ rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
rb_fiber_scheduler_set(Qnil);
}
else {
- state = error_handle(ec, state);
+ state = error_handle(ec, ec->errinfo, state);
}
EC_POP_TAG();
}
@@ -175,20 +179,21 @@ ruby_finalize(void)
int
ruby_cleanup(int ex)
{
- return rb_ec_cleanup(GET_EC(), ex);
+ return rb_ec_cleanup(GET_EC(), (enum ruby_tag_type)ex);
}
static int
-rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
+rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
{
int state;
- volatile VALUE errs[2] = { Qundef, Qundef };
- int nerr;
+ volatile VALUE save_error = Qundef;
+ volatile int sysex = EXIT_SUCCESS;
+ volatile int signaled = 0;
rb_thread_t *th = rb_ec_thread_ptr(ec);
rb_thread_t *const volatile th0 = th;
- volatile int sysex = EXIT_SUCCESS;
volatile int step = 0;
- volatile int ex = ex0;
+ volatile VALUE message = Qnil;
+ VALUE buf;
rb_threadptr_interrupt(th);
rb_threadptr_check_signal(th);
@@ -198,60 +203,61 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(ec); });
step_0: step++;
- errs[1] = ec->errinfo;
+ save_error = ec->errinfo;
if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
- ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
+ ruby_init_stack(&message);
+ /* exits with failure but silently when an exception raised
+ * here */
SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec));
step_1: step++;
+ VALUE err = ec->errinfo;
+ volatile int mode0 = 0, mode1 = 0;
+ if (err != save_error && !NIL_P(err)) {
+ mode0 = exiting_split(err, &sysex, &signaled);
+ }
+
+ /* exceptions after here will be ignored */
+
+ /* build error message including causes */
+ err = ATOMIC_VALUE_EXCHANGE(save_error, Qnil);
+
+ if (!NIL_P(err) && !THROW_DATA_P(err)) {
+ mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
+ if (mode1 & EXITING_WITH_MESSAGE) {
+ buf = rb_str_new(NULL, 0);
+ SAVE_ROOT_JMPBUF(th, rb_ec_error_print_detailed(ec, err, buf, Qundef));
+ message = buf;
+ }
+ }
+
+ step_2: step++;
/* protect from Thread#raise */
th->status = THREAD_KILLED;
- errs[0] = ec->errinfo;
SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
+
+ step_3: step++;
+ if (!NIL_P(buf = message)) {
+ warn_print_str(buf);
+ }
+ else if (!NIL_OR_UNDEF_P(err = save_error) ||
+ (ex != TAG_NONE && !((mode0|mode1) & EXITING_WITH_STATUS))) {
+ sysex = error_handle(ec, err, ex);
+ }
}
else {
th = th0;
switch (step) {
case 0: goto step_0;
case 1: goto step_1;
- }
- if (ex == 0) ex = state;
- }
- ec->errinfo = errs[1];
- sysex = error_handle(ec, ex);
-
- state = 0;
- for (nerr = 0; nerr < numberof(errs); ++nerr) {
- VALUE err = ATOMIC_VALUE_EXCHANGE(errs[nerr], Qnil);
- VALUE sig;
-
- if (!RTEST(err)) continue;
-
- /* ec->errinfo contains a NODE while break'ing */
- if (THROW_DATA_P(err)) continue;
-
- if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
- sysex = sysexit_status(err);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSignal)) {
- VALUE sig = rb_ivar_get(err, id_signo);
- state = NUM2INT(sig);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSystemCallError) &&
- FIXNUM_P(sig = rb_attr_get(err, id_signo))) {
- state = NUM2INT(sig);
- break;
- }
- else if (sysex == EXIT_SUCCESS) {
- sysex = EXIT_FAILURE;
+ case 2: goto step_2;
+ case 3: goto step_3;
}
}
- mjit_finish(true); // We still need ISeqs here.
+ mjit_finish(true); // We still need ISeqs here, so it's before rb_ec_finalize().
rb_ec_finalize(ec);
@@ -262,7 +268,10 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
th = th0;
rb_thread_stop_timer_thread();
ruby_vm_destruct(th->vm);
- if (state) ruby_default_signal(state);
+ // For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
+ rb_jit_cont_finish();
+
+ if (signaled) ruby_default_signal(signaled);
return sysex;
}
@@ -314,7 +323,7 @@ ruby_run_node(void *n)
rb_execution_context_t *ec = GET_EC();
int status;
if (!ruby_executable_node(n, &status)) {
- rb_ec_cleanup(ec, 0);
+ rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
return status;
}
ruby_init_stack((void *)&status);
@@ -511,7 +520,7 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
nocause = 0;
nocircular = 1;
}
- if (*cause == Qundef) {
+ if (UNDEF_P(*cause)) {
if (nocause) {
*cause = Qnil;
nocircular = 1;
@@ -527,13 +536,17 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
rb_raise(rb_eTypeError, "exception object expected");
}
- if (!nocircular && !NIL_P(*cause) && *cause != Qundef && *cause != mesg) {
+ if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) {
+#if 0 /* maybe critical for some cases */
+ rb_exc_check_circular_cause(*cause);
+#else
VALUE c = *cause;
while (!NIL_P(c = rb_attr_get(c, id_cause))) {
if (c == mesg) {
rb_raise(rb_eArgError, "circular causes");
}
}
+#endif
}
return mesg;
}
@@ -546,18 +559,18 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
const char *file = rb_source_location_cstr(&line);
const char *const volatile file0 = file;
- if ((file && !NIL_P(mesg)) || (cause != Qundef)) {
+ if ((file && !NIL_P(mesg)) || !UNDEF_P(cause)) {
volatile int state = 0;
EC_PUSH_TAG(ec);
if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) {
VALUE bt = rb_get_backtrace(mesg);
- if (!NIL_P(bt) || cause == Qundef) {
+ if (!NIL_P(bt) || UNDEF_P(cause)) {
if (OBJ_FROZEN(mesg)) {
mesg = rb_obj_dup(mesg);
}
}
- if (cause != Qundef && !THROW_DATA_P(cause)) {
+ if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) {
exc_setup_cause(mesg, cause);
}
if (NIL_P(bt)) {
@@ -630,10 +643,14 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
void
rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
- if (cause == Qundef) {
+ if (UNDEF_P(cause)) {
cause = get_ec_errinfo(ec);
}
if (cause != mesg) {
+ if (THROW_DATA_P(cause)) {
+ cause = Qnil;
+ }
+
rb_ivar_set(mesg, id_cause, cause);
}
}
@@ -725,7 +742,7 @@ rb_f_raise(int argc, VALUE *argv)
argc = extract_raise_opts(argc, argv, opts);
if (argc == 0) {
- if (*cause != Qundef) {
+ if (!UNDEF_P(*cause)) {
rb_raise(rb_eArgError, "only cause is given with no arguments");
}
err = get_errinfo();
@@ -801,7 +818,7 @@ make_exception(int argc, const VALUE *argv, int isstr)
if (NIL_P(mesg)) {
mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]);
}
- if (mesg == Qundef) {
+ if (UNDEF_P(mesg)) {
rb_raise(rb_eTypeError, "exception class/object expected");
}
if (!rb_obj_is_kind_of(mesg, rb_eException)) {
@@ -1760,6 +1777,16 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
return obj;
}
+VALUE
+rb_top_main_class(const char *method)
+{
+ VALUE klass = GET_THREAD()->top_wrapper;
+
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
+}
+
/*
* call-seq:
* include(module, ...) -> self
@@ -1772,13 +1799,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *th = GET_THREAD();
-
- if (th->top_wrapper) {
- rb_warning("main.include in the wrapped load is effective only in wrapper module");
- return rb_mod_include(argc, argv, th->top_wrapper);
- }
- return rb_mod_include(argc, argv, rb_cObject);
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
/*
@@ -1792,10 +1813,12 @@ top_include(int argc, VALUE *argv, VALUE self)
static VALUE
top_using(VALUE self, VALUE module)
{
- const rb_cref_t *cref = rb_vm_cref();
+ const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());;
rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ rb_thread_t *th = GET_THREAD();
- if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
+ if ((th->top_wrapper ? CREF_NEXT(cref) : cref) ||
+ (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
}
if (rb_block_given_p()) {
diff --git a/eval_error.c b/eval_error.c
index 6582b805aa..9806683000 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -73,12 +73,6 @@ set_backtrace(VALUE info, VALUE bt)
rb_check_funcall(info, set_backtrace, 1, &bt);
}
-static void
-error_print(rb_execution_context_t *ec)
-{
- rb_ec_error_print(ec, ec->errinfo);
-}
-
#define CSI_BEGIN "\033["
#define CSI_SGR "m"
@@ -297,6 +291,17 @@ show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse,
}
void
+rb_exc_check_circular_cause(VALUE exc)
+{
+ VALUE cause = exc, shown_causes = 0;
+ do {
+ if (shown_cause_p(cause, &shown_causes)) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ } while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
+}
+
+void
rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
{
volatile VALUE eclass;
@@ -306,7 +311,7 @@ rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VA
if (NIL_P(errinfo))
return;
- if (errat == Qundef) {
+ if (UNDEF_P(errat)) {
errat = Qnil;
}
eclass = CLASS_OF(errinfo);
@@ -338,13 +343,13 @@ rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VA
}
}
-void
-rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
+static void
+rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
{
volatile uint8_t raised_flag = ec->raised_flag;
volatile VALUE errat = Qundef;
- volatile VALUE emesg = Qundef;
volatile bool written = false;
+ volatile VALUE emesg = emesg0;
VALUE opt = rb_hash_new();
VALUE highlight = rb_stderr_tty_p() ? Qtrue : Qfalse;
@@ -358,14 +363,14 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
if (EC_EXEC_TAG() == TAG_NONE) {
errat = rb_get_backtrace(errinfo);
}
- if (emesg == Qundef) {
+ if (UNDEF_P(emesg)) {
emesg = Qnil;
emesg = rb_get_detailed_message(errinfo, opt);
}
if (!written) {
written = true;
- rb_error_write(errinfo, emesg, errat, Qnil, opt, highlight, Qfalse);
+ rb_error_write(errinfo, emesg, errat, str, opt, highlight, Qfalse);
}
EC_POP_TAG();
@@ -373,6 +378,12 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
rb_ec_raised_set(ec, raised_flag);
}
+void
+rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
+{
+ rb_ec_error_print_detailed(ec, errinfo, Qnil, Qundef);
+}
+
#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
#define undef_mesg(v) ( \
is_mod ? \
@@ -429,11 +440,63 @@ sysexit_status(VALUE err)
return NUM2INT(st);
}
+enum {
+ EXITING_WITH_MESSAGE = 1,
+ EXITING_WITH_STATUS = 2,
+ EXITING_WITH_SIGNAL = 4
+};
+static int
+exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
+{
+ int ex = EXIT_SUCCESS;
+ VALUE signo;
+ int sig = 0;
+ int result = 0;
+
+ if (NIL_P(errinfo)) return 0;
+
+ if (THROW_DATA_P(errinfo)) {
+ int throw_state = ((const struct vm_throw_data *)errinfo)->throw_state;
+ ex = throw_state & VM_THROW_STATE_MASK;
+ result |= EXITING_WITH_STATUS;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
+ ex = sysexit_status(errinfo);
+ result |= EXITING_WITH_STATUS;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSignal)) {
+ signo = rb_ivar_get(errinfo, id_signo);
+ sig = FIX2INT(signo);
+ result |= EXITING_WITH_SIGNAL;
+ /* no message when exiting by signal */
+ if (signo == INT2FIX(SIGSEGV) || !rb_obj_is_instance_of(errinfo, rb_eSignal))
+ /* except for SEGV and subclasses */
+ result |= EXITING_WITH_MESSAGE;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
+ FIXNUM_P(signo = rb_attr_get(errinfo, id_signo))) {
+ sig = FIX2INT(signo);
+ result |= EXITING_WITH_SIGNAL;
+ /* no message when exiting by error to be mapped to signal */
+ }
+ else {
+ ex = EXIT_FAILURE;
+ result |= EXITING_WITH_STATUS | EXITING_WITH_MESSAGE;
+ }
+
+ if (exitcode && (result & EXITING_WITH_STATUS))
+ *exitcode = ex;
+ if (sigstatus && (result & EXITING_WITH_SIGNAL))
+ *sigstatus = sig;
+
+ return result;
+}
+
#define unknown_longjmp_status(status) \
rb_bug("Unknown longjmp status %d", status)
static int
-error_handle(rb_execution_context_t *ec, int ex)
+error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
{
int status = EXIT_FAILURE;
@@ -469,26 +532,13 @@ error_handle(rb_execution_context_t *ec, int ex)
error_pos(Qnil);
warn_print("unexpected throw\n");
break;
- case TAG_RAISE: {
- VALUE errinfo = ec->errinfo;
- if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
- status = sysexit_status(errinfo);
- }
- else if (rb_obj_is_instance_of(errinfo, rb_eSignal) &&
- rb_ivar_get(errinfo, id_signo) != INT2FIX(SIGSEGV)) {
- /* no message when exiting by signal */
+ case TAG_RAISE:
+ if (!(exiting_split(errinfo, &status, NULL) & EXITING_WITH_MESSAGE)) {
+ break;
}
- else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
- FIXNUM_P(rb_attr_get(errinfo, id_signo))) {
- /* no message when exiting by error to be mapped to signal */
- }
- else {
- rb_ec_error_print(ec, errinfo);
- }
- break;
- }
+ /* fallthrough */
case TAG_FATAL:
- error_print(ec);
+ rb_ec_error_print(ec, errinfo);
break;
default:
unknown_longjmp_status(ex);
diff --git a/eval_jump.c b/eval_jump.c
index a6139bc27b..e8e74f4e70 100644
--- a/eval_jump.c
+++ b/eval_jump.c
@@ -121,7 +121,7 @@ rb_ec_exec_end_proc(rb_execution_context_t * ec)
}
else {
EC_TMPPOP_TAG();
- error_handle(ec, state);
+ error_handle(ec, ec->errinfo, state);
if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo;
EC_REPUSH_TAG();
goto again;
diff --git a/ext/-test-/marshal/internal_ivar/internal_ivar.c b/ext/-test-/marshal/internal_ivar/internal_ivar.c
index de0cf711aa..b2188f737a 100644
--- a/ext/-test-/marshal/internal_ivar/internal_ivar.c
+++ b/ext/-test-/marshal/internal_ivar/internal_ivar.c
@@ -36,10 +36,7 @@ Init_internal_ivar(void)
VALUE newclass = rb_define_class_under(mMarshal, "InternalIVar", rb_cObject);
id_normal_ivar = rb_intern_const("normal");
-#if 0
- /* leave id_internal_ivar being 0 */
- id_internal_ivar = rb_make_internal_id();
-#endif
+ id_internal_ivar = rb_intern_const("K");
id_encoding_short = rb_intern_const("E");
rb_define_method(newclass, "initialize", init, 3);
rb_define_method(newclass, "normal", get_normal, 0);
diff --git a/ext/-test-/num2int/num2int.c b/ext/-test-/num2int/num2int.c
index 3aec3ccf3b..63a441fda6 100644
--- a/ext/-test-/num2int/num2int.c
+++ b/ext/-test-/num2int/num2int.c
@@ -4,7 +4,7 @@ static VALUE
test_num2short(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", NUM2SHORT(num));
+ snprintf(buf, sizeof(buf), "%d", NUM2SHORT(num));
return rb_str_new_cstr(buf);
}
@@ -12,7 +12,7 @@ static VALUE
test_num2ushort(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", NUM2USHORT(num));
+ snprintf(buf, sizeof(buf), "%u", NUM2USHORT(num));
return rb_str_new_cstr(buf);
}
@@ -20,7 +20,7 @@ static VALUE
test_num2int(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", NUM2INT(num));
+ snprintf(buf, sizeof(buf), "%d", NUM2INT(num));
return rb_str_new_cstr(buf);
}
@@ -28,7 +28,7 @@ static VALUE
test_num2uint(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", NUM2UINT(num));
+ snprintf(buf, sizeof(buf), "%u", NUM2UINT(num));
return rb_str_new_cstr(buf);
}
@@ -36,7 +36,7 @@ static VALUE
test_num2long(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%ld", NUM2LONG(num));
+ snprintf(buf, sizeof(buf), "%ld", NUM2LONG(num));
return rb_str_new_cstr(buf);
}
@@ -44,7 +44,7 @@ static VALUE
test_num2ulong(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%lu", NUM2ULONG(num));
+ snprintf(buf, sizeof(buf), "%lu", NUM2ULONG(num));
return rb_str_new_cstr(buf);
}
@@ -53,7 +53,7 @@ static VALUE
test_num2ll(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%"PRI_LL_PREFIX"d", NUM2LL(num));
+ snprintf(buf, sizeof(buf), "%"PRI_LL_PREFIX"d", NUM2LL(num));
return rb_str_new_cstr(buf);
}
@@ -61,7 +61,7 @@ static VALUE
test_num2ull(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%"PRI_LL_PREFIX"u", NUM2ULL(num));
+ snprintf(buf, sizeof(buf), "%"PRI_LL_PREFIX"u", NUM2ULL(num));
return rb_str_new_cstr(buf);
}
#endif
@@ -70,7 +70,7 @@ static VALUE
test_fix2short(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", FIX2SHORT(num));
+ snprintf(buf, sizeof(buf), "%d", FIX2SHORT(num));
return rb_str_new_cstr(buf);
}
@@ -78,7 +78,7 @@ static VALUE
test_fix2int(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%d", FIX2INT(num));
+ snprintf(buf, sizeof(buf), "%d", FIX2INT(num));
return rb_str_new_cstr(buf);
}
@@ -86,7 +86,7 @@ static VALUE
test_fix2uint(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%u", FIX2UINT(num));
+ snprintf(buf, sizeof(buf), "%u", FIX2UINT(num));
return rb_str_new_cstr(buf);
}
@@ -94,7 +94,7 @@ static VALUE
test_fix2long(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%ld", FIX2LONG(num));
+ snprintf(buf, sizeof(buf), "%ld", FIX2LONG(num));
return rb_str_new_cstr(buf);
}
@@ -102,7 +102,7 @@ static VALUE
test_fix2ulong(VALUE obj, VALUE num)
{
char buf[128];
- sprintf(buf, "%lu", FIX2ULONG(num));
+ snprintf(buf, sizeof(buf), "%lu", FIX2ULONG(num));
return rb_str_new_cstr(buf);
}
diff --git a/ext/-test-/random/bad_version.c b/ext/-test-/random/bad_version.c
new file mode 100644
index 0000000000..dae63a6d19
--- /dev/null
+++ b/ext/-test-/random/bad_version.c
@@ -0,0 +1,135 @@
+#include "ruby/random.h"
+
+#if RUBY_RANDOM_INTERFACE_VERSION_MAJOR < RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX
+# define DEFINE_VERSION_MAX 1
+#else
+# define DEFINE_VERSION_MAX 0
+#endif
+
+NORETURN(static void must_not_reach(void));
+static void
+must_not_reach(void)
+{
+ rb_raise(rb_eTypeError, "must not reach");
+}
+
+NORETURN(static void bad_version_init(rb_random_t *, const uint32_t *, size_t));
+static void
+bad_version_init(rb_random_t *rnd, const uint32_t *buf, size_t len)
+{
+ must_not_reach();
+}
+
+NORETURN(static void bad_version_init_int32(rb_random_t *, uint32_t));
+RB_RANDOM_DEFINE_INIT_INT32_FUNC(bad_version)
+
+NORETURN(static void bad_version_get_bytes(rb_random_t *, void *, size_t));
+static void
+bad_version_get_bytes(rb_random_t *rnd, void *p, size_t n)
+{
+ must_not_reach();
+}
+
+NORETURN(static uint32_t bad_version_get_int32(rb_random_t *));
+static uint32_t
+bad_version_get_int32(rb_random_t *rnd)
+{
+ must_not_reach();
+ UNREACHABLE_RETURN(0);
+}
+
+static VALUE
+bad_version_alloc(VALUE klass, const rb_data_type_t *type)
+{
+ rb_random_t *rnd;
+ VALUE obj = TypedData_Make_Struct(klass, rb_random_t, type, rnd);
+ rb_random_base_init(rnd);
+ return obj;
+}
+
+/* version 0 */
+static const rb_random_interface_t random_version_zero_if;
+
+static rb_random_data_type_t version_zero_type = {
+ "random/version_zero",
+ {
+ rb_random_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ },
+ RB_RANDOM_PARENT,
+ (void *)&random_version_zero_if,
+ RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+version_zero_alloc(VALUE klass)
+{
+ return bad_version_alloc(klass, &version_zero_type);
+}
+
+static void
+init_version_zero(VALUE mod, VALUE base)
+{
+ VALUE c = rb_define_class_under(mod, "VersionZero", base);
+ rb_define_alloc_func(c, version_zero_alloc);
+ RB_RANDOM_DATA_INIT_PARENT(version_zero_type);
+}
+
+#if DEFINE_VERSION_MAX
+/* version max */
+static const rb_random_interface_t random_version_max_if;
+static rb_random_data_type_t version_max_type = {
+ "random/version_max",
+ {
+ rb_random_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ },
+ RB_RANDOM_PARENT,
+ (void *)&random_version_max_if,
+ RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+version_max_alloc(VALUE klass)
+{
+ return bad_version_alloc(klass, &version_max_type);
+}
+
+static void
+init_version_max(VALUE mod, VALUE base)
+{
+ VALUE c = rb_define_class_under(mod, "VersionMax", base);
+ rb_define_alloc_func(c, version_max_alloc);
+ RB_RANDOM_DATA_INIT_PARENT(version_max_type);
+}
+#else
+static void
+init_version_max(mod, base)
+{
+}
+#endif
+
+void
+Init_random_bad_version(VALUE mod, VALUE base)
+{
+ init_version_zero(mod, base);
+ init_version_max(mod, base);
+}
+
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 0
+static const rb_random_interface_t random_version_zero_if = {
+ 0,
+ RB_RANDOM_INTERFACE_DEFINE(bad_version)
+};
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+
+#if DEFINE_VERSION_MAX
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX
+static const rb_random_interface_t random_version_max_if = {
+ 0,
+ RB_RANDOM_INTERFACE_DEFINE(bad_version)
+};
+#undef RUBY_RANDOM_INTERFACE_VERSION_MAJOR
+#endif
diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend
index 602526cf7b..f2cbf7fc14 100644
--- a/ext/-test-/random/depend
+++ b/ext/-test-/random/depend
@@ -1,4 +1,164 @@
# AUTOGENERATED DEPENDENCIES START
+bad_version.o: $(RUBY_EXTCONF_H)
+bad_version.o: $(arch_hdrdir)/ruby/config.h
+bad_version.o: $(hdrdir)/ruby/assert.h
+bad_version.o: $(hdrdir)/ruby/backward.h
+bad_version.o: $(hdrdir)/ruby/backward/2/assume.h
+bad_version.o: $(hdrdir)/ruby/backward/2/attributes.h
+bad_version.o: $(hdrdir)/ruby/backward/2/bool.h
+bad_version.o: $(hdrdir)/ruby/backward/2/inttypes.h
+bad_version.o: $(hdrdir)/ruby/backward/2/limits.h
+bad_version.o: $(hdrdir)/ruby/backward/2/long_long.h
+bad_version.o: $(hdrdir)/ruby/backward/2/stdalign.h
+bad_version.o: $(hdrdir)/ruby/backward/2/stdarg.h
+bad_version.o: $(hdrdir)/ruby/defines.h
+bad_version.o: $(hdrdir)/ruby/intern.h
+bad_version.o: $(hdrdir)/ruby/internal/abi.h
+bad_version.o: $(hdrdir)/ruby/internal/anyargs.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+bad_version.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+bad_version.o: $(hdrdir)/ruby/internal/assume.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/artificial.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/cold.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/const.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/error.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/format.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noalias.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noinline.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/pure.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/restrict.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/warning.h
+bad_version.o: $(hdrdir)/ruby/internal/attr/weakref.h
+bad_version.o: $(hdrdir)/ruby/internal/cast.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+bad_version.o: $(hdrdir)/ruby/internal/compiler_since.h
+bad_version.o: $(hdrdir)/ruby/internal/config.h
+bad_version.o: $(hdrdir)/ruby/internal/constant_p.h
+bad_version.o: $(hdrdir)/ruby/internal/core.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rarray.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rbasic.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rbignum.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rclass.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rdata.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rfile.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rhash.h
+bad_version.o: $(hdrdir)/ruby/internal/core/robject.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rregexp.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rstring.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rstruct.h
+bad_version.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+bad_version.o: $(hdrdir)/ruby/internal/ctype.h
+bad_version.o: $(hdrdir)/ruby/internal/dllexport.h
+bad_version.o: $(hdrdir)/ruby/internal/dosish.h
+bad_version.o: $(hdrdir)/ruby/internal/error.h
+bad_version.o: $(hdrdir)/ruby/internal/eval.h
+bad_version.o: $(hdrdir)/ruby/internal/event.h
+bad_version.o: $(hdrdir)/ruby/internal/fl_type.h
+bad_version.o: $(hdrdir)/ruby/internal/gc.h
+bad_version.o: $(hdrdir)/ruby/internal/glob.h
+bad_version.o: $(hdrdir)/ruby/internal/globals.h
+bad_version.o: $(hdrdir)/ruby/internal/has/attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/builtin.h
+bad_version.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+bad_version.o: $(hdrdir)/ruby/internal/has/extension.h
+bad_version.o: $(hdrdir)/ruby/internal/has/feature.h
+bad_version.o: $(hdrdir)/ruby/internal/has/warning.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/array.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/bignum.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/class.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/compar.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/complex.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/cont.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/dir.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/enum.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/error.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/eval.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/file.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/gc.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/hash.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/io.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/load.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/marshal.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/numeric.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/object.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/parse.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/proc.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/process.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/random.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/range.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/rational.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/re.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/ruby.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/select.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/signal.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/string.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/struct.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/thread.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/time.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/variable.h
+bad_version.o: $(hdrdir)/ruby/internal/intern/vm.h
+bad_version.o: $(hdrdir)/ruby/internal/interpreter.h
+bad_version.o: $(hdrdir)/ruby/internal/iterator.h
+bad_version.o: $(hdrdir)/ruby/internal/memory.h
+bad_version.o: $(hdrdir)/ruby/internal/method.h
+bad_version.o: $(hdrdir)/ruby/internal/module.h
+bad_version.o: $(hdrdir)/ruby/internal/newobj.h
+bad_version.o: $(hdrdir)/ruby/internal/rgengc.h
+bad_version.o: $(hdrdir)/ruby/internal/scan_args.h
+bad_version.o: $(hdrdir)/ruby/internal/special_consts.h
+bad_version.o: $(hdrdir)/ruby/internal/static_assert.h
+bad_version.o: $(hdrdir)/ruby/internal/stdalign.h
+bad_version.o: $(hdrdir)/ruby/internal/stdbool.h
+bad_version.o: $(hdrdir)/ruby/internal/symbol.h
+bad_version.o: $(hdrdir)/ruby/internal/value.h
+bad_version.o: $(hdrdir)/ruby/internal/value_type.h
+bad_version.o: $(hdrdir)/ruby/internal/variable.h
+bad_version.o: $(hdrdir)/ruby/internal/warning_push.h
+bad_version.o: $(hdrdir)/ruby/internal/xmalloc.h
+bad_version.o: $(hdrdir)/ruby/missing.h
+bad_version.o: $(hdrdir)/ruby/random.h
+bad_version.o: $(hdrdir)/ruby/ruby.h
+bad_version.o: $(hdrdir)/ruby/st.h
+bad_version.o: $(hdrdir)/ruby/subst.h
+bad_version.o: bad_version.c
init.o: $(RUBY_EXTCONF_H)
init.o: $(arch_hdrdir)/ruby/config.h
init.o: $(hdrdir)/ruby.h
diff --git a/ext/-test-/random/loop.c b/ext/-test-/random/loop.c
index 0572096403..b789ab1d01 100644
--- a/ext/-test-/random/loop.c
+++ b/ext/-test-/random/loop.c
@@ -13,6 +13,7 @@ static const rb_random_interface_t random_loop_if = {
RB_RANDOM_INTERFACE_DEFINE_WITH_REAL(loop)
};
+RB_RANDOM_DEFINE_INIT_INT32_FUNC(loop)
static size_t
random_loop_memsize(const void *ptr)
{
diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend
index 8729695886..ce977821b8 100644
--- a/ext/-test-/rational/depend
+++ b/ext/-test-/rational/depend
@@ -174,5 +174,6 @@ rat.o: $(top_srcdir)/internal/static_assert.h
rat.o: $(top_srcdir)/internal/vm.h
rat.o: $(top_srcdir)/internal/warnings.h
rat.o: $(top_srcdir)/ruby_assert.h
+rat.o: $(top_srcdir)/shape.h
rat.o: rat.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index 2374319fe3..64f079251d 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -12,13 +12,13 @@ bug_s_fstring(VALUE self, VALUE str)
VALUE
bug_s_rb_enc_interned_str(VALUE self, VALUE encoding)
{
- return rb_enc_interned_str("foo", 3, RDATA(encoding)->data);
+ return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
VALUE
bug_s_rb_enc_str_new(VALUE self, VALUE encoding)
{
- return rb_enc_str_new("foo", 3, RDATA(encoding)->data);
+ return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
void
diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c
index 219cea404c..049da2cdb5 100644
--- a/ext/-test-/string/set_len.c
+++ b/ext/-test-/string/set_len.c
@@ -7,8 +7,18 @@ bug_str_set_len(VALUE str, VALUE len)
return str;
}
+static VALUE
+bug_str_append(VALUE str, VALUE addendum)
+{
+ StringValue(addendum);
+ rb_str_modify_expand(str, RSTRING_LEN(addendum));
+ memcpy(RSTRING_END(str), RSTRING_PTR(addendum), RSTRING_LEN(addendum));
+ return str;
+}
+
void
Init_string_set_len(VALUE klass)
{
rb_define_method(klass, "set_len", bug_str_set_len, 1);
+ rb_define_method(klass, "append", bug_str_append, 1);
}
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index b3ef70a2c9..d6ea35c615 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -7,9 +7,7 @@
*/
/* #define BIGDECIMAL_DEBUG 1 */
-#ifdef BIGDECIMAL_DEBUG
-# define BIGDECIMAL_ENABLE_VPRINT 1
-#endif
+
#include "bigdecimal.h"
#include "ruby/util.h"
@@ -61,6 +59,13 @@ static ID id_to_r;
static ID id_eq;
static ID id_half;
+#define RBD_NUM_ROUNDING_MODES 11
+
+static struct {
+ ID id;
+ uint8_t mode;
+} rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
+
/* MACRO's to guard objects from GC by keeping them in stack */
#ifdef RBIMPL_ATTR_MAYBE_UNUSED
#define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0
@@ -102,10 +107,164 @@ static ID id_half;
# define RB_OBJ_STRING(obj) StringValueCStr(obj)
#endif
+#ifndef MAYBE_UNUSED
+# define MAYBE_UNUSED(x) x
+#endif
+
#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
/*
+ * ================== Memory allocation ============================
+ */
+
+#ifdef BIGDECIMAL_DEBUG
+static size_t rbd_allocation_count = 0; /* Memory allocation counter */
+static inline void
+atomic_allocation_count_inc(void)
+{
+ RUBY_ATOMIC_SIZE_INC(rbd_allocation_count);
+}
+static inline void
+atomic_allocation_count_dec_nounderflow(void)
+{
+ if (rbd_allocation_count == 0) return;
+ RUBY_ATOMIC_SIZE_DEC(rbd_allocation_count);
+}
+static void
+check_allocation_count_nonzero(void)
+{
+ if (rbd_allocation_count != 0) return;
+ rb_bug("[bigdecimal][rbd_free_struct] Too many memory free calls");
+}
+#else
+# define atomic_allocation_count_inc() /* nothing */
+# define atomic_allocation_count_dec_nounderflow() /* nothing */
+# define check_allocation_count_nonzero() /* nothing */
+#endif /* BIGDECIMAL_DEBUG */
+
+PUREFUNC(static inline size_t rbd_struct_size(size_t const));
+
+static inline size_t
+rbd_struct_size(size_t const internal_digits)
+{
+ size_t const frac_len = (internal_digits == 0) ? 1 : internal_digits;
+ return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
+}
+
+static inline Real *
+rbd_allocate_struct(size_t const internal_digits)
+{
+ size_t const size = rbd_struct_size(internal_digits);
+ Real *real = ruby_xcalloc(1, size);
+ atomic_allocation_count_inc();
+ real->MaxPrec = internal_digits;
+ return real;
+}
+
+static size_t
+rbd_calculate_internal_digits(size_t const digits, bool limit_precision)
+{
+ size_t const len = roomof(digits, BASE_FIG);
+ if (limit_precision) {
+ size_t const prec_limit = VpGetPrecLimit();
+ if (prec_limit > 0) {
+ /* NOTE: 2 more digits for rounding and division */
+ size_t const max_len = roomof(prec_limit, BASE_FIG) + 2;
+ if (len > max_len)
+ return max_len;
+ }
+ }
+
+ return len;
+}
+
+static inline Real *
+rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_precision)
+{
+ size_t const internal_digits = rbd_calculate_internal_digits(decimal_digits, limit_precision);
+ return rbd_allocate_struct(internal_digits);
+}
+
+static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp);
+
+static Real *
+rbd_reallocate_struct(Real *real, size_t const internal_digits)
+{
+ size_t const size = rbd_struct_size(internal_digits);
+ VALUE obj = real ? real->obj : 0;
+ Real *new_real = (Real *)ruby_xrealloc(real, size);
+ new_real->MaxPrec = internal_digits;
+ if (obj) {
+ new_real->obj = 0;
+ BigDecimal_wrap_struct(obj, new_real);
+ }
+ return new_real;
+}
+
+static void
+rbd_free_struct(Real *real)
+{
+ if (real != NULL) {
+ check_allocation_count_nonzero();
+ ruby_xfree(real);
+ atomic_allocation_count_dec_nounderflow();
+ }
+}
+
+#define NewZero rbd_allocate_struct_zero
+static Real *
+rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
+ VpSetZero(real, sign);
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits));
+#define NewZeroLimited rbd_allocate_struct_zero_limited
+static inline Real *
+rbd_allocate_struct_zero_limited(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero(sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits));
+#define NewZeroNolimit rbd_allocate_struct_zero_nolimit
+static inline Real *
+rbd_allocate_struct_zero_nolimit(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero(sign, digits, false);
+}
+
+#define NewOne rbd_allocate_struct_one
+static Real *
+rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
+ VpSetOne(real);
+ if (sign < 0)
+ VpSetSign(real, VP_SIGN_NEGATIVE_FINITE);
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits));
+#define NewOneLimited rbd_allocate_struct_one_limited
+static inline Real *
+rbd_allocate_struct_one_limited(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one(sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits));
+#define NewOneNolimit rbd_allocate_struct_one_nolimit
+static inline Real *
+rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one(sign, digits, false);
+}
+
+/*
* ================== Ruby Interface part ==========================
*/
#define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
@@ -120,10 +279,7 @@ static VALUE VpCheckGetValue(Real *p);
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
static int VpLimitRound(Real *c, size_t ixDigit);
static Real *VpCopy(Real *pv, Real const* const x);
-
-#ifdef BIGDECIMAL_ENABLE_VPRINT
static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
-#endif
/*
* **** BigDecimal part ****
@@ -138,7 +294,7 @@ static VALUE BigDecimal_negative_zero(void);
static void
BigDecimal_delete(void *pv)
{
- VpFree(pv);
+ rbd_free_struct(pv);
}
static size_t
@@ -161,6 +317,60 @@ static const rb_data_type_t BigDecimal_data_type = {
#endif
};
+static Real *
+rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_zero(sign, digits, limit_precision);
+ if (real != NULL) {
+ VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
+ BigDecimal_wrap_struct(obj, real);
+ }
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits));
+#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap
+static inline Real *
+rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits));
+#define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap
+static inline Real *
+rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, false);
+}
+
+static Real *
+rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
+{
+ Real *real = rbd_allocate_struct_one(sign, digits, limit_precision);
+ if (real != NULL) {
+ VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
+ BigDecimal_wrap_struct(obj, real);
+ }
+ return real;
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits));
+#define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap
+static inline Real *
+rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true);
+}
+
+MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits));
+#define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap
+static inline Real *
+rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits)
+{
+ return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, false);
+}
+
static inline int
is_kind_of_BigDecimal(VALUE const v)
{
@@ -214,7 +424,7 @@ GetVpValueWithPrec(VALUE v, long prec, int must)
case T_FIXNUM: {
char szD[128];
- sprintf(szD, "%ld", FIX2LONG(v));
+ snprintf(szD, 128, "%ld", FIX2LONG(v));
v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must);
break;
}
@@ -249,7 +459,7 @@ SomeOneMayDoIt:
return NULL; /* NULL means to coerce */
}
-static Real*
+static inline Real*
GetVpValue(VALUE v, int must)
{
return GetVpValueWithPrec(v, -1, must);
@@ -264,7 +474,7 @@ GetVpValue(VALUE v, int must)
* BigDecimal.double_fig # => 16
*
*/
-static VALUE
+static inline VALUE
BigDecimal_double_fig(VALUE self)
{
return INT2FIX(VpDblFig());
@@ -486,15 +696,15 @@ BigDecimal_precision_scale(VALUE self)
*
* Returns the number of decimal significant digits in +self+.
*
- * BigDecimal("0").scale # => 0
- * BigDecimal("1").scale # => 1
- * BigDecimal("1.1").scale # => 2
- * BigDecimal("3.1415").scale # => 5
- * BigDecimal("-1e20").precision # => 1
- * BigDecimal("1e-20").precision # => 1
- * BigDecimal("Infinity").scale # => 0
- * BigDecimal("-Infinity").scale # => 0
- * BigDecimal("NaN").scale # => 0
+ * BigDecimal("0").n_significant_digits # => 0
+ * BigDecimal("1").n_significant_digits # => 1
+ * BigDecimal("1.1").n_significant_digits # => 2
+ * BigDecimal("3.1415").n_significant_digits # => 5
+ * BigDecimal("-1e20").n_significant_digits # => 1
+ * BigDecimal("1e-20").n_significant_digits # => 1
+ * BigDecimal("Infinity").n_significant_digits # => 0
+ * BigDecimal("-Infinity").n_significant_digits # => 0
+ * BigDecimal("NaN").n_significant_digits # => 0
*/
static VALUE
BigDecimal_n_significant_digits(VALUE self)
@@ -573,13 +783,15 @@ BigDecimal_dump(int argc, VALUE *argv, VALUE self)
char *psz;
VALUE dummy;
volatile VALUE dump;
+ size_t len;
rb_scan_args(argc, argv, "01", &dummy);
GUARD_OBJ(vp,GetVpValue(self, 1));
dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
psz = RSTRING_PTR(dump);
- sprintf(psz, "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
- VpToString(vp, psz+strlen(psz), 0, 0);
+ snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
+ len = strlen(psz);
+ VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0);
rb_str_resize(dump, strlen(psz));
return dump;
}
@@ -623,18 +835,19 @@ check_rounding_mode_option(VALUE const opts)
assert(RB_TYPE_P(opts, T_HASH));
if (NIL_P(opts))
- goto noopt;
+ goto no_opt;
mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef);
if (mode == Qundef || NIL_P(mode))
- goto noopt;
+ goto no_opt;
if (SYMBOL_P(mode))
mode = rb_sym2str(mode);
else if (!RB_TYPE_P(mode, T_STRING)) {
- VALUE str_mode = rb_check_string_type(mode);
- if (NIL_P(str_mode)) goto invalid;
- mode = str_mode;
+ VALUE str_mode = rb_check_string_type(mode);
+ if (NIL_P(str_mode))
+ goto invalid;
+ mode = str_mode;
}
s = RSTRING_PTR(mode);
l = RSTRING_LEN(mode);
@@ -652,13 +865,11 @@ check_rounding_mode_option(VALUE const opts)
default:
break;
}
+
invalid:
- if (NIL_P(mode))
- rb_raise(rb_eArgError, "invalid rounding mode: nil");
- else
- rb_raise(rb_eArgError, "invalid rounding mode: %"PRIsVALUE, mode);
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", mode);
- noopt:
+ no_opt:
return VpGetRoundMode();
}
@@ -667,34 +878,23 @@ check_rounding_mode(VALUE const v)
{
unsigned short sw;
ID id;
- switch (TYPE(v)) {
- case T_SYMBOL:
- id = SYM2ID(v);
- if (id == id_up)
- return VP_ROUND_UP;
- if (id == id_down || id == id_truncate)
- return VP_ROUND_DOWN;
- if (id == id_half_up || id == id_default)
- return VP_ROUND_HALF_UP;
- if (id == id_half_down)
- return VP_ROUND_HALF_DOWN;
- if (id == id_half_even || id == id_banker)
- return VP_ROUND_HALF_EVEN;
- if (id == id_ceiling || id == id_ceil)
- return VP_ROUND_CEIL;
- if (id == id_floor)
- return VP_ROUND_FLOOR;
- rb_raise(rb_eArgError, "invalid rounding mode");
-
- default:
- break;
+ if (RB_TYPE_P(v, T_SYMBOL)) {
+ int i;
+ id = SYM2ID(v);
+ for (i = 0; i < RBD_NUM_ROUNDING_MODES; ++i) {
+ if (rbd_rounding_modes[i].id == id) {
+ return rbd_rounding_modes[i].mode;
+ }
+ }
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
}
-
- sw = NUM2USHORT(v);
- if (!VpIsRoundMode(sw)) {
- rb_raise(rb_eArgError, "invalid rounding mode");
+ else {
+ sw = NUM2USHORT(v);
+ if (!VpIsRoundMode(sw)) {
+ rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
+ }
+ return sw;
}
- return sw;
}
/* call-seq:
@@ -933,11 +1133,17 @@ GetAddSubPrec(Real *a, Real *b)
return mx;
}
-static SIGNED_VALUE
-GetPrecisionInt(VALUE v)
+static inline SIGNED_VALUE
+check_int_precision(VALUE v)
{
SIGNED_VALUE n;
- n = NUM2INT(v);
+#if SIZEOF_VALUE <= SIZEOF_LONG
+ n = (SIGNED_VALUE)NUM2LONG(v);
+#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
+ n = (SIGNED_VALUE)NUM2LL(v);
+#else
+# error SIZEOF_VALUE is too large
+#endif
if (n < 0) {
rb_raise(rb_eArgError, "negative precision");
}
@@ -979,26 +1185,12 @@ VpCreateRbObject(size_t mx, const char *str, bool raise_exception)
return VpNewRbClass(mx, str, rb_cBigDecimal, true, raise_exception);
}
-#define VpAllocReal(prec) (Real *)VpMemAlloc(offsetof(Real, frac) + (prec) * sizeof(DECDIG))
-
-static Real *
-VpReallocReal(Real *pv, size_t prec)
-{
- VALUE obj = pv ? pv->obj : 0;
- Real *new_pv = (Real *)VpMemRealloc(pv, offsetof(Real, frac) + prec * sizeof(DECDIG));
- if (obj) {
- new_pv->obj = 0;
- BigDecimal_wrap_struct(obj, new_pv);
- }
- return new_pv;
-}
-
static Real *
VpCopy(Real *pv, Real const* const x)
{
assert(x != NULL);
- pv = VpReallocReal(pv, x->MaxPrec);
+ pv = rbd_reallocate_struct(pv, x->MaxPrec);
pv->MaxPrec = x->MaxPrec;
pv->Prec = x->Prec;
pv->exponent = x->exponent;
@@ -1119,7 +1311,7 @@ BigDecimal_to_f(VALUE self)
str = rb_str_new(0, VpNumOfChars(p, "E"));
buf = RSTRING_PTR(str);
- VpToString(p, buf, 0, 0);
+ VpToString(p, buf, RSTRING_LEN(str), 0, 0);
errno = 0;
d = strtod(buf, 0);
if (errno == ERANGE) {
@@ -1276,17 +1468,17 @@ BigDecimal_add(VALUE self, VALUE r)
mx = GetAddSubPrec(a, b);
if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, 1);
+ GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
+ VpAddSub(c, a, b, 1);
}
else {
- GUARD_OBJ(c, VpCreateRbObject(mx * (VpBaseFig() + 1), "0", true));
- if(!mx) {
- VpSetInf(c, VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, 1);
- }
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
+ if (!mx) {
+ VpSetInf(c, VpGetSign(a));
+ }
+ else {
+ VpAddSub(c, a, b, 1);
+ }
}
return VpCheckGetValue(c);
}
@@ -1331,17 +1523,17 @@ BigDecimal_sub(VALUE self, VALUE r)
mx = GetAddSubPrec(a,b);
if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, -1);
+ GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
+ VpAddSub(c, a, b, -1);
}
else {
- GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
- if (!mx) {
- VpSetInf(c,VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, -1);
- }
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx *(VpBaseFig() + 1)));
+ if (!mx) {
+ VpSetInf(c,VpGetSign(a));
+ }
+ else {
+ VpAddSub(c, a, b, -1);
+ }
}
return VpCheckGetValue(c);
}
@@ -1581,7 +1773,7 @@ BigDecimal_neg(VALUE self)
ENTER(5);
Real *c, *a;
GUARD_OBJ(a, GetVpValue(self, 1));
- GUARD_OBJ(c, VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, a->Prec *(VpBaseFig() + 1)));
VpAsgn(c, a, -1);
return VpCheckGetValue(c);
}
@@ -1608,7 +1800,7 @@ BigDecimal_mult(VALUE self, VALUE r)
SAVE(b);
mx = a->Prec + b->Prec;
- GUARD_OBJ(c, VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
VpMult(c, a, b);
return VpCheckGetValue(c);
}
@@ -1655,8 +1847,8 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
- GUARD_OBJ((*c), VpCreateRbObject(mx + 2*BASE_FIG, "#0", true));
- GUARD_OBJ((*res), VpCreateRbObject((mx + 1)*2 + 2*BASE_FIG, "#0", true));
+ GUARD_OBJ((*c), NewZeroWrapNolimit(1, mx + 2*BASE_FIG));
+ GUARD_OBJ((*res), NewZeroWrapNolimit(1, (mx + 1)*2 + 2*BASE_FIG));
VpDivd(*c, *res, a, b);
return Qnil;
@@ -1721,7 +1913,7 @@ BigDecimal_quo(int argc, VALUE *argv, VALUE self)
argc = rb_scan_args(argc, argv, "11", &value, &digits);
if (argc > 1) {
- n = GetPrecisionInt(digits);
+ n = check_int_precision(digits);
}
if (n > 0) {
@@ -1811,12 +2003,12 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
- GUARD_OBJ(c, VpCreateRbObject(mx + 2*BASE_FIG, "0", true));
- GUARD_OBJ(res, VpCreateRbObject(mx*2 + 2*BASE_FIG, "#0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx + 2*BASE_FIG));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, mx*2 + 2*BASE_FIG));
VpDivd(c, res, a, b);
mx = c->Prec * BASE_FIG;
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
VpActiveRound(d, c, VP_ROUND_DOWN, 0);
VpMult(res, d, b);
@@ -1824,10 +2016,10 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
if (!VpIsZero(c) && (VpGetSign(a) * VpGetSign(b) < 0)) {
/* result adjustment for negative case */
- res = VpReallocReal(res, d->MaxPrec);
+ res = rbd_reallocate_struct(res, d->MaxPrec);
res->MaxPrec = d->MaxPrec;
VpAddSub(res, d, VpOne(), -1);
- GUARD_OBJ(d, VpCreateRbObject(GetAddSubPrec(c, b) * 2*BASE_FIG, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, GetAddSubPrec(c, b) * 2*BASE_FIG));
VpAddSub(d, c, b, 1);
*div = res;
*mod = d;
@@ -1891,17 +2083,17 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
SAVE(b);
mx = (a->MaxPrec + b->MaxPrec) *VpBaseFig();
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(res, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(rr, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(ff, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
+ GUARD_OBJ(rr, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
+ GUARD_OBJ(ff, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
VpDivd(c, res, a, b);
mx = c->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(f, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
+ GUARD_OBJ(f, NewZeroWrapLimited(1, mx));
VpActiveRound(d, c, VP_ROUND_DOWN, 0); /* 0: round off */
@@ -1986,7 +2178,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
}
/* div in BigDecimal sense */
- ix = GetPrecisionInt(n);
+ ix = check_int_precision(n);
if (ix == 0) {
return BigDecimal_div(self, b);
}
@@ -1997,7 +2189,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
size_t b_prec = ix;
size_t pl = VpSetPrecLimit(0);
- GUARD_OBJ(cv, VpCreateRbObject(mx + VpBaseFig(), "0", true));
+ GUARD_OBJ(cv, NewZeroWrapLimited(1, mx + VpBaseFig()));
GUARD_OBJ(av, GetVpValue(self, 1));
/* TODO: I want to refactor this precision control for a float value later
* by introducing an implicit conversion function instead of
@@ -2008,7 +2200,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
GUARD_OBJ(bv, GetVpValueWithPrec(b, b_prec, 1));
mx = av->Prec + bv->Prec + 2;
if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1;
- GUARD_OBJ(res, VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0", true));
+ GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx * 2 + 2)*VpBaseFig()));
VpDivd(cv, res, av, bv);
VpSetPrecLimit(pl);
VpLeftRound(cv, VpGetRoundMode(), ix);
@@ -2091,7 +2283,7 @@ BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_add(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2121,7 +2313,7 @@ BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_sub(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2164,7 +2356,7 @@ BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
+ SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_mult(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -2196,7 +2388,7 @@ BigDecimal_abs(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpAsgn(c, a, 1);
VpChangeSign(c, 1);
return VpCheckGetValue(c);
@@ -2219,9 +2411,10 @@ BigDecimal_sqrt(VALUE self, VALUE nFig)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- n = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG;
+ n = check_int_precision(nFig);
+ n += VpDblFig() + VpBaseFig();
if (mx <= n) mx = n;
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSqrt(c, a);
return VpCheckGetValue(c);
}
@@ -2237,7 +2430,7 @@ BigDecimal_fix(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
return VpCheckGetValue(c);
}
@@ -2310,7 +2503,7 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self)
pl = VpSetPrecLimit(0);
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, sw, iLoc);
if (round_to_int) {
@@ -2356,7 +2549,7 @@ BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
if (argc == 0) {
@@ -2376,7 +2569,7 @@ BigDecimal_frac(VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpFrac(c, a);
return VpCheckGetValue(c);
}
@@ -2416,7 +2609,7 @@ BigDecimal_floor(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc);
#ifdef BIGDECIMAL_DEBUG
@@ -2462,7 +2655,7 @@ BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
+ GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_CEIL, iLoc);
if (argc == 0) {
@@ -2566,10 +2759,10 @@ BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
psz = RSTRING_PTR(str);
if (fmt) {
- VpToFString(vp, psz, mc, fPlus);
+ VpToFString(vp, psz, RSTRING_LEN(str), mc, fPlus);
}
else {
- VpToString (vp, psz, mc, fPlus);
+ VpToString (vp, psz, RSTRING_LEN(str), mc, fPlus);
}
rb_str_resize(str, strlen(psz));
return str;
@@ -2611,7 +2804,7 @@ BigDecimal_split(VALUE self)
GUARD_OBJ(vp, GetVpValue(self, 1));
str = rb_str_new(0, VpNumOfChars(vp, "E"));
psz1 = RSTRING_PTR(str);
- VpSzMantissa(vp, psz1);
+ VpSzMantissa(vp, psz1, RSTRING_LEN(str));
s = 1;
if(psz1[0] == '-') {
size_t len = strlen(psz1 + 1);
@@ -2660,7 +2853,7 @@ BigDecimal_inspect(VALUE self)
nc = VpNumOfChars(vp, "E");
str = rb_str_new(0, nc);
- VpToString(vp, RSTRING_PTR(str), 0, 0);
+ VpToString(vp, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0);
rb_str_resize(str, strlen(RSTRING_PTR(str)));
return str;
}
@@ -2770,7 +2963,7 @@ bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n)
volatile VALUE obj = exp->obj;
if (VpIsZero(exp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
}
log_x = BigMath_log(x->obj, SSIZET2NUM(n+1));
@@ -2808,9 +3001,9 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec);
if (VpIsNaN(x)) {
- y = VpCreateRbObject(n, "0", true);
- RB_GC_GUARD(y->obj);
- VpSetNaN(y);
+ y = NewZeroWrapLimited(1, n);
+ VpSetNaN(y);
+ RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
}
@@ -2879,136 +3072,126 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
if (VpIsZero(x)) {
- if (is_negative(vexp)) {
- y = VpCreateRbObject(n, "#0", true);
- RB_GC_GUARD(y->obj);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-0) ** (-even_integer) -> Infinity */
- VpSetPosInf(y);
- }
- else {
- /* (-0) ** (-odd_integer) -> -Infinity */
- VpSetNegInf(y);
- }
- }
- else {
- /* (-0) ** (-non_integer) -> Infinity */
- VpSetPosInf(y);
- }
- }
- else {
- /* (+0) ** (-num) -> Infinity */
- VpSetPosInf(y);
- }
+ if (is_negative(vexp)) {
+ y = NewZeroWrapNolimit(1, n);
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ /* (-0) ** (-even_integer) -> Infinity */
+ VpSetPosInf(y);
+ }
+ else {
+ /* (-0) ** (-odd_integer) -> -Infinity */
+ VpSetNegInf(y);
+ }
+ }
+ else {
+ /* (-0) ** (-non_integer) -> Infinity */
+ VpSetPosInf(y);
+ }
+ }
+ else {
+ /* (+0) ** (-num) -> Infinity */
+ VpSetPosInf(y);
+ }
+ RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
- }
- else if (is_zero(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
+ }
+ else if (is_zero(vexp)) {
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
}
if (is_zero(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
}
else if (is_one(vexp)) {
- return self;
+ return self;
}
if (VpIsInf(x)) {
- if (is_negative(vexp)) {
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-Infinity) ** (-even_integer) -> +0 */
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- else {
- /* (-Infinity) ** (-odd_integer) -> -0 */
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- }
- else {
- /* (-Infinity) ** (-non_integer) -> -0 */
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
- else {
- y = VpCreateRbObject(n, "0", true);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- VpSetPosInf(y);
- }
- else {
- VpSetNegInf(y);
- }
- }
- else {
- /* TODO: support complex */
- rb_raise(rb_eMathDomainError,
- "a non-integral exponent for a negative base");
- }
- }
- else {
- VpSetPosInf(y);
- }
+ if (is_negative(vexp)) {
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ /* (-Infinity) ** (-even_integer) -> +0 */
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ else {
+ /* (-Infinity) ** (-odd_integer) -> -0 */
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ }
+ else {
+ /* (-Infinity) ** (-non_integer) -> -0 */
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
+ else {
+ y = NewZeroWrapLimited(1, n);
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ if (is_integer(vexp)) {
+ if (is_even(vexp)) {
+ VpSetPosInf(y);
+ }
+ else {
+ VpSetNegInf(y);
+ }
+ }
+ else {
+ /* TODO: support complex */
+ rb_raise(rb_eMathDomainError,
+ "a non-integral exponent for a negative base");
+ }
+ }
+ else {
+ VpSetPosInf(y);
+ }
return VpCheckGetValue(y);
- }
+ }
}
if (exp != NULL) {
- return bigdecimal_power_by_bigdecimal(x, exp, n);
+ return bigdecimal_power_by_bigdecimal(x, exp, n);
}
else if (RB_TYPE_P(vexp, T_BIGNUM)) {
- VALUE abs_value = BigDecimal_abs(self);
- if (is_one(abs_value)) {
- return VpCheckGetValue(VpCreateRbObject(n, "1", true));
- }
- else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
- if (is_negative(vexp)) {
- y = VpCreateRbObject(n, "0", true);
- if (is_even(vexp)) {
- VpSetInf(y, VpGetSign(x));
- }
- else {
- VpSetInf(y, -VpGetSign(x));
- }
+ VALUE abs_value = BigDecimal_abs(self);
+ if (is_one(abs_value)) {
+ return VpCheckGetValue(NewOneWrapLimited(1, n));
+ }
+ else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
+ if (is_negative(vexp)) {
+ y = NewZeroWrapLimited(1, n);
+ VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
- else {
- if (is_positive(vexp)) {
- y = VpCreateRbObject(n, "0", true);
- if (is_even(vexp)) {
- VpSetInf(y, VpGetSign(x));
- }
- else {
- VpSetInf(y, -VpGetSign(x));
- }
+ }
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
+ else {
+ if (is_positive(vexp)) {
+ y = NewZeroWrapLimited(1, n);
+ VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(VpCreateRbObject(n, "-0", true));
- }
- else {
- return VpCheckGetValue(VpCreateRbObject(n, "0", true));
- }
- }
+ }
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
+ return VpCheckGetValue(NewZeroWrapLimited(-1, n));
+ }
+ else {
+ return VpCheckGetValue(NewZeroWrapLimited(1, n));
+ }
+ }
}
int_exp = FIX2LONG(vexp);
@@ -3017,15 +3200,15 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
if (ma == 0) ma = 1;
if (VpIsDef(x)) {
- mp = x->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(y, VpCreateRbObject(mp * (ma + 1), "0", true));
+ mp = x->Prec * (VpBaseFig() + 1);
+ GUARD_OBJ(y, NewZeroWrapLimited(1, mp * (ma + 1)));
}
else {
- GUARD_OBJ(y, VpCreateRbObject(1, "0", true));
+ GUARD_OBJ(y, NewZeroWrapLimited(1, 1));
}
VpPowerByInt(y, x, int_exp);
if (!NIL_P(prec) && VpIsDef(y)) {
- VpMidRound(y, VpGetRoundMode(), n);
+ VpMidRound(y, VpGetRoundMode(), n);
}
return VpCheckGetValue(y);
}
@@ -3114,7 +3297,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
Real *vp;
if (uval == 0) {
- vp = VpAllocReal(1);
+ vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
vp->Prec = 1;
vp->exponent = 1;
@@ -3122,7 +3305,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
vp->frac[0] = 0;
}
else if (uval < BASE) {
- vp = VpAllocReal(1);
+ vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
vp->Prec = 1;
vp->exponent = 1;
@@ -3148,7 +3331,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r
}
const size_t exp = len + ntz;
- vp = VpAllocReal(len);
+ vp = rbd_allocate_struct(len);
vp->MaxPrec = len;
vp->Prec = len;
vp->exponent = exp;
@@ -3500,6 +3683,9 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
* in the value, the result is rounded to that number of digits,
* according to the current rounding mode; see BigDecimal.mode.
*
+ * When +ndigits+ is 0, the number of digits to correctly represent a float number
+ * is determined automatically.
+ *
* Returns +value+ converted to a \BigDecimal, depending on the type of +value+:
*
* - Integer, Float, Rational, Complex, or BigDecimal: converted directly:
@@ -3606,8 +3792,10 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self)
/* Returns the sign of the value.
*
- * Returns a positive value if > 0, a negative value if < 0, and a
- * zero if == 0.
+ * Returns a positive value if > 0, a negative value if < 0.
+ * It behaves the same with zeros -
+ * it returns a positive value for a positive zero (BigDecimal('0')) and
+ * a negative value for a negative zero (BigDecimal('-0')).
*
* The specific value returned indicates the type and sign of the BigDecimal,
* as follows:
@@ -3771,18 +3959,16 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
return VpCheckGetValue(GetVpValueWithPrec(INT2FIX(0), prec, 1));
}
else {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real* vy = NewZeroWrapNolimit(1, prec);
VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
RB_GC_GUARD(vy->obj);
return VpCheckGetValue(vy);
}
}
else if (nan) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
- VpSetNaN(vy);
- RB_GC_GUARD(vy->obj);
+ Real* vy = NewZeroWrapNolimit(1, prec);
+ VpSetNaN(vy);
+ RB_GC_GUARD(vy->obj);
return VpCheckGetValue(vy);
}
else if (vx == NULL) {
@@ -3800,7 +3986,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
VpSetSign(vx, 1);
}
- one = VpCheckGetValue(VpCreateRbObject(1, "1", true));
+ one = VpCheckGetValue(NewOneWrapLimited(1, 1));
y = one;
d = y;
i = 1;
@@ -3927,15 +4113,13 @@ get_vp_value:
break;
}
if (infinite && !negative) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real *vy = NewZeroWrapNolimit(1, prec);
RB_GC_GUARD(vy->obj);
VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
return VpCheckGetValue(vy);
}
else if (nan) {
- Real* vy;
- vy = VpCreateRbObject(prec, "#0", true);
+ Real* vy = NewZeroWrapNolimit(1, prec);
RB_GC_GUARD(vy->obj);
VpSetNaN(vy);
return VpCheckGetValue(vy);
@@ -3949,7 +4133,7 @@ get_vp_value:
}
x = VpCheckGetValue(vx);
- RB_GC_GUARD(one) = VpCheckGetValue(VpCreateRbObject(1, "1", true));
+ RB_GC_GUARD(one) = VpCheckGetValue(NewOneWrapLimited(1, 1));
RB_GC_GUARD(two) = VpCheckGetValue(VpCreateRbObject(1, "2", true));
n = prec + BIGDECIMAL_DOUBLE_FIGURES;
@@ -4414,20 +4598,31 @@ Init_bigdecimal(void)
rb_define_singleton_method(rb_mBigMath, "exp", BigMath_s_exp, 2);
rb_define_singleton_method(rb_mBigMath, "log", BigMath_s_log, 2);
- id_up = rb_intern_const("up");
- id_down = rb_intern_const("down");
- id_truncate = rb_intern_const("truncate");
- id_half_up = rb_intern_const("half_up");
- id_default = rb_intern_const("default");
- id_half_down = rb_intern_const("half_down");
- id_half_even = rb_intern_const("half_even");
- id_banker = rb_intern_const("banker");
- id_ceiling = rb_intern_const("ceiling");
- id_ceil = rb_intern_const("ceil");
- id_floor = rb_intern_const("floor");
+#define ROUNDING_MODE(i, name, value) \
+ id_##name = rb_intern_const(#name); \
+ rbd_rounding_modes[i].id = id_##name; \
+ rbd_rounding_modes[i].mode = value;
+
+ ROUNDING_MODE(0, up, RBD_ROUND_UP);
+ ROUNDING_MODE(1, down, RBD_ROUND_DOWN);
+ ROUNDING_MODE(2, half_up, RBD_ROUND_HALF_UP);
+ ROUNDING_MODE(3, half_down, RBD_ROUND_HALF_DOWN);
+ ROUNDING_MODE(4, ceil, RBD_ROUND_CEIL);
+ ROUNDING_MODE(5, floor, RBD_ROUND_FLOOR);
+ ROUNDING_MODE(6, half_even, RBD_ROUND_HALF_EVEN);
+
+ ROUNDING_MODE(7, default, RBD_ROUND_DEFAULT);
+ ROUNDING_MODE(8, truncate, RBD_ROUND_TRUNCATE);
+ ROUNDING_MODE(9, banker, RBD_ROUND_BANKER);
+ ROUNDING_MODE(10, ceiling, RBD_ROUND_CEILING);
+
+#undef ROUNDING_MODE
+
id_to_r = rb_intern_const("to_r");
id_eq = rb_intern_const("==");
id_half = rb_intern_const("half");
+
+ (void)VPrint; /* suppress unused warning */
}
/*
@@ -4447,7 +4642,7 @@ static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */
#endif /* BIGDECIMAL_DEBUG */
static Real *VpConstOne; /* constant 1.0 */
-static Real *VpPt5; /* constant 0.5 */
+static Real *VpConstPt5; /* constant 0.5 */
#define maxnr 100UL /* Maximum iterations for calculating sqrt. */
/* used in VpSqrt() */
@@ -4478,42 +4673,6 @@ static int VpRdup(Real *m, size_t ind_m);
static int gnAlloc = 0; /* Memory allocation counter */
#endif /* BIGDECIMAL_DEBUG */
-VP_EXPORT void *
-VpMemAlloc(size_t mb)
-{
- void *p = xmalloc(mb);
- memset(p, 0, mb);
-#ifdef BIGDECIMAL_DEBUG
- gnAlloc++; /* Count allocation call */
-#endif /* BIGDECIMAL_DEBUG */
- return p;
-}
-
-VP_EXPORT void *
-VpMemRealloc(void *ptr, size_t mb)
-{
- return xrealloc(ptr, mb);
-}
-
-VP_EXPORT void
-VpFree(Real *pv)
-{
- if (pv != NULL) {
- xfree(pv);
-#ifdef BIGDECIMAL_DEBUG
- gnAlloc--; /* Decrement allocation count */
- if (gnAlloc == 0) {
- printf(" *************** All memories allocated freed ****************\n");
- /*getchar();*/
- }
- if (gnAlloc < 0) {
- printf(" ??????????? Too many memory free calls(%d) ?????????????\n", gnAlloc);
- /*getchar();*/
- }
-#endif /* BIGDECIMAL_DEBUG */
- }
-}
-
/*
* EXCEPTION Handling.
*/
@@ -4902,9 +5061,13 @@ VpInit(DECDIG BaseVal)
/* Setup +/- Inf NaN -0 */
VpGetDoubleNegZero();
- /* Allocates Vp constants. */
- VpConstOne = VpAlloc(1UL, "1", 1, 1);
- VpPt5 = VpAlloc(1UL, ".5", 1, 1);
+ /* Const 1.0 */
+ VpConstOne = NewOneNolimit(1, 1);
+
+ /* Const 0.5 */
+ VpConstPt5 = NewOneNolimit(1, 1);
+ VpConstPt5->exponent = 0;
+ VpConstPt5->frac[0] = 5*BASE1;
#ifdef BIGDECIMAL_DEBUG
gnAlloc = 0;
@@ -4993,7 +5156,7 @@ bigdecimal_parse_special_string(const char *str)
p = str + table[i].len;
while (*p && ISSPACE(*p)) ++p;
if (*p == '\0') {
- Real *vp = VpAllocReal(1);
+ Real *vp = rbd_allocate_struct(1);
vp->MaxPrec = 1;
switch (table[i].sign) {
default:
@@ -5017,11 +5180,11 @@ bigdecimal_parse_special_string(const char *str)
/*
* Allocates variable.
* [Input]
- * mx ... allocation unit, if zero then mx is determined by szVal.
- * The mx is the number of effective digits can to be stored.
- * szVal ... value assigned(char). If szVal==NULL,then zero is assumed.
- * If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7),
- * full precision specified by szVal is allocated.
+ * mx ... The number of decimal digits to be allocated, if zero then mx is determined by szVal.
+ * The mx will be the number of significant digits can to be stored.
+ * szVal ... The value assigned(char). If szVal==NULL, then zero is assumed.
+ * If szVal[0]=='#' then MaxPrec is not affected by the precision limit
+ * so that the full precision specified by szVal is allocated.
*
* [Returns]
* Pointer to the newly allocated variable, or
@@ -5032,48 +5195,40 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
{
const char *orig_szVal = szVal;
size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc;
+ size_t len;
char v, *psz;
int sign=1;
Real *vp = NULL;
- size_t mf = VpGetPrecLimit();
VALUE buf;
- mx = (mx + BASE_FIG - 1) / BASE_FIG; /* Determine allocation unit. */
- if (mx == 0) ++mx;
-
- if (szVal) {
- /* Skipping leading spaces */
- while (ISSPACE(*szVal)) szVal++;
-
- /* Processing the leading one `#` */
- if (*szVal != '#') {
- if (mf) {
- mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */
- if (mx > mf) {
- mx = mf;
- }
- }
- }
- else {
- ++szVal;
- }
- }
- else {
+ if (szVal == NULL) {
return_zero:
/* necessary to be able to store */
/* at least mx digits. */
/* szVal==NULL ==> allocate zero value. */
- vp = VpAllocReal(mx);
- vp->MaxPrec = mx; /* set max precision */
+ vp = rbd_allocate_struct(mx);
+ vp->MaxPrec = rbd_calculate_internal_digits(mx, false); /* Must false */
VpSetZero(vp, 1); /* initialize vp to zero. */
return vp;
}
+ /* Skipping leading spaces */
+ while (ISSPACE(*szVal)) szVal++;
+
/* Check on Inf & NaN */
if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
return vp;
}
+ /* Processing the leading one `#` */
+ if (*szVal != '#') {
+ len = rbd_calculate_internal_digits(mx, true);
+ }
+ else {
+ len = rbd_calculate_internal_digits(mx, false);
+ ++szVal;
+ }
+
/* Scanning digits */
/* A buffer for keeping scanned digits */
@@ -5235,11 +5390,11 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
/* units for szVal[] */
- if (mx == 0) mx = 1;
- nalloc = Max(nalloc, mx);
- mx = nalloc;
- vp = VpAllocReal(mx);
- vp->MaxPrec = mx; /* set max precision */
+ if (len == 0) len = 1;
+ nalloc = Max(nalloc, len);
+ len = nalloc;
+ vp = rbd_allocate_struct(len);
+ vp->MaxPrec = len; /* set max precision */
VpSetZero(vp, sign);
VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
rb_str_resize(buf, 0);
@@ -5804,7 +5959,7 @@ VpMult(Real *c, Real *a, Real *b)
if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */
w = c;
- c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0", 1, 1);
+ c = NewZeroNolimit(1, (size_t)((MxIndAB + 1) * BASE_FIG));
MxIndC = MxIndAB;
}
@@ -5812,8 +5967,8 @@ VpMult(Real *c, Real *a, Real *b)
c->exponent = a->exponent; /* set exponent */
if (!AddExponent(c, b->exponent)) {
- if (w) VpFree(c);
- return 0;
+ if (w) rbd_free_struct(c);
+ return 0;
}
VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */
carry = 0;
@@ -5863,10 +6018,10 @@ VpMult(Real *c, Real *a, Real *b)
}
}
if (w != NULL) { /* free work variable */
- VpNmlz(c);
- VpAsgn(w, c, 1);
- VpFree(c);
- c = w;
+ VpNmlz(c);
+ VpAsgn(w, c, 1);
+ rbd_free_struct(c);
+ c = w;
}
else {
VpLimitRound(c,0);
@@ -6235,7 +6390,6 @@ Exit:
* Note: % must not appear more than once
* a ... VP variable to be printed
*/
-#ifdef BIGDECIMAL_ENABLE_VPRINT
static int
VPrint(FILE *fp, const char *cntl_chr, Real *a)
{
@@ -6248,95 +6402,94 @@ VPrint(FILE *fp, const char *cntl_chr, Real *a)
/* nc : number of characters printed */
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
while (*(cntl_chr + j)) {
- if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') {
- nc = 0;
- if (VpIsNaN(a)) {
- fprintf(fp, SZ_NaN);
- nc += 8;
- }
- else if (VpIsPosInf(a)) {
- fprintf(fp, SZ_INF);
- nc += 8;
- }
- else if (VpIsNegInf(a)) {
- fprintf(fp, SZ_NINF);
- nc += 9;
- }
- else if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) {
- fprintf(fp, "-");
- ++nc;
- }
- nc += fprintf(fp, "0.");
- switch (*(cntl_chr + j + 1)) {
- default:
- break;
+ if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') {
+ nc = 0;
+ if (VpIsNaN(a)) {
+ fprintf(fp, SZ_NaN);
+ nc += 8;
+ }
+ else if (VpIsPosInf(a)) {
+ fprintf(fp, SZ_INF);
+ nc += 8;
+ }
+ else if (VpIsNegInf(a)) {
+ fprintf(fp, SZ_NINF);
+ nc += 9;
+ }
+ else if (!VpIsZero(a)) {
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ fprintf(fp, "-");
+ ++nc;
+ }
+ nc += fprintf(fp, "0.");
+ switch (*(cntl_chr + j + 1)) {
+ default:
+ break;
- case '0': case 'z':
- ZeroSup = 0;
- ++j;
- sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10;
- break;
- }
- for (i = 0; i < a->Prec; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
- /* as 0.00xx will not */
- /* be printed. */
- ++nd;
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- if (nd >= sep) { /* print ' ' after every 10 digits */
- nd = 0;
- nc += fprintf(fp, " ");
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a));
- nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec);
- }
- else {
- nc += fprintf(fp, "0.0");
- }
- }
- else {
- ++nc;
- if (*(cntl_chr + j) == '\\') {
- switch (*(cntl_chr + j + 1)) {
- case 'n':
- fprintf(fp, "\n");
- ++j;
- break;
- case 't':
- fprintf(fp, "\t");
- ++j;
- break;
- case 'b':
- fprintf(fp, "\n");
- ++j;
- break;
- default:
- fprintf(fp, "%c", *(cntl_chr + j));
- break;
- }
- }
- else {
- fprintf(fp, "%c", *(cntl_chr + j));
- if (*(cntl_chr + j) == '%') ++j;
- }
- }
- j++;
+ case '0': case 'z':
+ ZeroSup = 0;
+ ++j;
+ sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10;
+ break;
+ }
+ for (i = 0; i < a->Prec; ++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
+ /* as 0.00xx will not */
+ /* be printed. */
+ ++nd;
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ if (nd >= sep) { /* print ' ' after every 10 digits */
+ nd = 0;
+ nc += fprintf(fp, " ");
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a));
+ nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec);
+ }
+ else {
+ nc += fprintf(fp, "0.0");
+ }
+ }
+ else {
+ ++nc;
+ if (*(cntl_chr + j) == '\\') {
+ switch (*(cntl_chr + j + 1)) {
+ case 'n':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ case 't':
+ fprintf(fp, "\t");
+ ++j;
+ break;
+ case 'b':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ default:
+ fprintf(fp, "%c", *(cntl_chr + j));
+ break;
+ }
+ }
+ else {
+ fprintf(fp, "%c", *(cntl_chr + j));
+ if (*(cntl_chr + j) == '%') ++j;
+ }
+ }
+ j++;
}
return (int)nc;
}
-#endif
static void
VpFormatSt(char *psz, size_t fFmt)
@@ -6381,188 +6534,254 @@ VpExponent10(Real *a)
}
VP_EXPORT void
-VpSzMantissa(Real *a,char *psz)
+VpSzMantissa(Real *a, char *buf, size_t buflen)
{
size_t i, n, ZeroSup;
DECDIG_DBL m, e, nn;
if (VpIsNaN(a)) {
- sprintf(psz, SZ_NaN);
- return;
+ snprintf(buf, buflen, SZ_NaN);
+ return;
}
if (VpIsPosInf(a)) {
- sprintf(psz, SZ_INF);
+ snprintf(buf, buflen, SZ_INF);
return;
}
if (VpIsNegInf(a)) {
- sprintf(psz, SZ_NINF);
+ snprintf(buf, buflen, SZ_NINF);
return;
}
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- n = a->Prec;
- for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- sprintf(psz, "%lu", (unsigned long)nn); /* The leading zero(s) */
- psz += strlen(psz);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- *psz = 0;
- while (psz[-1] == '0') *(--psz) = 0;
+ if (BIGDECIMAL_NEGATIVE_P(a)) *buf++ = '-';
+ n = a->Prec;
+ for (i = 0; i < n; ++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */
+ buf += strlen(buf);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ *buf = 0;
+ while (buf[-1] == '0') *(--buf) = 0;
}
else {
- if (VpIsPosZero(a)) sprintf(psz, "0");
- else sprintf(psz, "-0");
+ if (VpIsPosZero(a)) snprintf(buf, buflen, "0");
+ else snprintf(buf, buflen, "-0");
}
}
VP_EXPORT int
-VpToSpecialString(Real *a,char *psz,int fPlus)
+VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
if (VpIsNaN(a)) {
- sprintf(psz,SZ_NaN);
- return 1;
+ snprintf(buf, buflen, SZ_NaN);
+ return 1;
}
if (VpIsPosInf(a)) {
- if (fPlus == 1) {
- *psz++ = ' ';
- }
- else if (fPlus == 2) {
- *psz++ = '+';
- }
- sprintf(psz, SZ_INF);
- return 1;
+ if (fPlus == 1) {
+ *buf++ = ' ';
+ }
+ else if (fPlus == 2) {
+ *buf++ = '+';
+ }
+ snprintf(buf, buflen, SZ_INF);
+ return 1;
}
if (VpIsNegInf(a)) {
- sprintf(psz, SZ_NINF);
- return 1;
+ snprintf(buf, buflen, SZ_NINF);
+ return 1;
}
if (VpIsZero(a)) {
- if (VpIsPosZero(a)) {
- if (fPlus == 1) sprintf(psz, " 0.0");
- else if (fPlus == 2) sprintf(psz, "+0.0");
- else sprintf(psz, "0.0");
- }
- else sprintf(psz, "-0.0");
- return 1;
+ if (VpIsPosZero(a)) {
+ if (fPlus == 1) snprintf(buf, buflen, " 0.0");
+ else if (fPlus == 2) snprintf(buf, buflen, "+0.0");
+ else snprintf(buf, buflen, "0.0");
+ }
+ else snprintf(buf, buflen, "-0.0");
+ return 1;
}
return 0;
}
VP_EXPORT void
-VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
+VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n, ZeroSup;
DECDIG shift, m, e, nn;
- char *pszSav = psz;
+ char *p = buf;
+ size_t plen = buflen;
ssize_t ex;
- if (VpToSpecialString(a, psz, fPlus)) return;
+ if (VpToSpecialString(a, buf, buflen, fPlus)) return;
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- else if (fPlus == 1) *psz++ = ' ';
- else if (fPlus == 2) *psz++ = '+';
+#define ADVANCE(n) do { \
+ if (plen < n) goto overflow; \
+ p += n; \
+ plen -= n; \
+} while (0)
+
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ *p = '-';
+ ADVANCE(1);
+ }
+ else if (fPlus == 1) {
+ *p = ' ';
+ ADVANCE(1);
+ }
+ else if (fPlus == 2) {
+ *p = '+';
+ ADVANCE(1);
+ }
+
+ *p = '0'; ADVANCE(1);
+ *p = '.'; ADVANCE(1);
- *psz++ = '0';
- *psz++ = '.';
n = a->Prec;
for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- sprintf(psz, "%lu", (unsigned long)nn); /* The reading zero(s) */
- psz += strlen(psz);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ if (!ZeroSup || nn) {
+ /* The reading zero(s) */
+ size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn);
+ if (n > plen) goto overflow;
+ ADVANCE(n);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
}
+
ex = a->exponent * (ssize_t)BASE_FIG;
shift = BASE1;
while (a->frac[0] / shift == 0) {
- --ex;
- shift /= 10;
+ --ex;
+ shift /= 10;
}
- while (psz[-1] == '0') {
- *(--psz) = 0;
+ while (p - 1 > buf && p[-1] == '0') {
+ *(--p) = '\0';
+ ++plen;
}
- sprintf(psz, "e%"PRIdSIZE, ex);
- if (fFmt) VpFormatSt(pszSav, fFmt);
+ snprintf(p, plen, "e%"PRIdSIZE, ex);
+ if (fFmt) VpFormatSt(buf, fFmt);
+
+ overflow:
+ return;
+#undef ADVANCE
}
VP_EXPORT void
-VpToFString(Real *a, char *psz, size_t fFmt, int fPlus)
+VpToFString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n;
DECDIG m, e, nn;
- char *pszSav = psz;
+ char *p = buf;
+ size_t plen = buflen;
ssize_t ex;
- if (VpToSpecialString(a, psz, fPlus)) return;
+ if (VpToSpecialString(a, buf, buflen, fPlus)) return;
- if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
- else if (fPlus == 1) *psz++ = ' ';
- else if (fPlus == 2) *psz++ = '+';
+#define ADVANCE(n) do { \
+ if (plen < n) goto overflow; \
+ p += n; \
+ plen -= n; \
+} while (0)
+
+
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
+ *p = '-';
+ ADVANCE(1);
+ }
+ else if (fPlus == 1) {
+ *p = ' ';
+ ADVANCE(1);
+ }
+ else if (fPlus == 2) {
+ *p = '+';
+ ADVANCE(1);
+ }
n = a->Prec;
ex = a->exponent;
if (ex <= 0) {
- *psz++ = '0';*psz++ = '.';
- while (ex < 0) {
- for (i=0; i < BASE_FIG; ++i) *psz++ = '0';
- ++ex;
- }
- ex = -1;
+ *p = '0'; ADVANCE(1);
+ *p = '.'; ADVANCE(1);
+ while (ex < 0) {
+ for (i=0; i < BASE_FIG; ++i) {
+ *p = '0'; ADVANCE(1);
+ }
+ ++ex;
+ }
+ ex = -1;
}
for (i = 0; i < n; ++i) {
- --ex;
- if (i == 0 && ex >= 0) {
- sprintf(psz, "%lu", (unsigned long)a->frac[i]);
- psz += strlen(psz);
- }
- else {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- *psz++ = (char)(nn + '0');
- e = e - nn * m;
- m /= 10;
- }
- }
- if (ex == 0) *psz++ = '.';
+ --ex;
+ if (i == 0 && ex >= 0) {
+ size_t n = snprintf(p, plen, "%lu", (unsigned long)a->frac[i]);
+ if (n > plen) goto overflow;
+ ADVANCE(n);
+ }
+ else {
+ m = BASE1;
+ e = a->frac[i];
+ while (m) {
+ nn = e / m;
+ *p = (char)(nn + '0');
+ ADVANCE(1);
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ if (ex == 0) {
+ *p = '.';
+ ADVANCE(1);
+ }
}
while (--ex>=0) {
- m = BASE;
- while (m /= 10) *psz++ = '0';
- if (ex == 0) *psz++ = '.';
+ m = BASE;
+ while (m /= 10) {
+ *p = '0';
+ ADVANCE(1);
+ }
+ if (ex == 0) {
+ *p = '.';
+ ADVANCE(1);
+ }
+ }
+
+ *p = '\0';
+ while (p - 1 > buf && p[-1] == '0') {
+ *(--p) = '\0';
+ ++plen;
+ }
+ if (p - 1 > buf && p[-1] == '.') {
+ snprintf(p, plen, "0");
}
- *psz = 0;
- while (psz[-1] == '0') *(--psz) = 0;
- if (psz[-1] == '.') sprintf(psz, "0");
- if (fFmt) VpFormatSt(pszSav, fFmt);
+ if (fFmt) VpFormatSt(buf, fFmt);
+
+ overflow:
+ return;
+#undef ADVANCE
}
/*
@@ -6971,8 +7190,9 @@ VpSqrt(Real *y, Real *x)
if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec;
/* allocate temporally variables */
- f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1", 1, 1);
- r = VpAlloc((n + n) * (BASE_FIG + 2), "#1", 1, 1);
+ /* TODO: reconsider MaxPrec of f and r */
+ f = NewOneNolimit(1, y->MaxPrec * (BASE_FIG + 2));
+ r = NewOneNolimit(1, (n + n) * (BASE_FIG + 2));
nr = 0;
y_prec = y->MaxPrec;
@@ -6997,16 +7217,21 @@ VpSqrt(Real *y, Real *x)
f->MaxPrec = y->MaxPrec + 1;
n = (SIGNED_VALUE)(y_prec * BASE_FIG);
if (n < (SIGNED_VALUE)maxnr) n = (SIGNED_VALUE)maxnr;
+
+ /*
+ * Perform: y_{n+1} = (y_n - x/y_n) / 2
+ */
do {
- y->MaxPrec *= 2;
- if (y->MaxPrec > y_prec) y->MaxPrec = y_prec;
- f->MaxPrec = y->MaxPrec;
- VpDivd(f, r, x, y); /* f = x/y */
- VpAddSub(r, f, y, -1); /* r = f - y */
- VpMult(f, VpPt5, r); /* f = 0.5*r */
- if (VpIsZero(f)) goto converge;
- VpAddSub(r, f, y, 1); /* r = y + f */
- VpAsgn(y, r, 1); /* y = r */
+ y->MaxPrec *= 2;
+ if (y->MaxPrec > y_prec) y->MaxPrec = y_prec;
+ f->MaxPrec = y->MaxPrec;
+ VpDivd(f, r, x, y); /* f = x/y */
+ VpAddSub(r, f, y, -1); /* r = f - y */
+ VpMult(f, VpConstPt5, r); /* f = 0.5*r */
+ if (VpIsZero(f))
+ goto converge;
+ VpAddSub(r, f, y, 1); /* r = y + f */
+ VpAsgn(y, r, 1); /* y = r */
} while (++nr < n);
#ifdef BIGDECIMAL_DEBUG
@@ -7031,8 +7256,8 @@ converge:
y->MaxPrec = y_prec;
Exit:
- VpFree(f);
- VpFree(r);
+ rbd_free_struct(f);
+ rbd_free_struct(r);
return 1;
}
@@ -7423,9 +7648,10 @@ VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n)
}
/* Allocate working variables */
+ /* TODO: reconsider MaxPrec of w1 and w2 */
+ w1 = NewZeroNolimit(1, (y->MaxPrec + 2) * BASE_FIG);
+ w2 = NewZeroNolimit(1, (w1->MaxPrec * 2 + 1) * BASE_FIG);
- w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0", 1, 1);
- w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0", 1, 1);
/* calculation start */
VpAsgn(y, x, 1);
@@ -7454,8 +7680,8 @@ Exit:
printf(" n=%"PRIdVALUE"\n", n);
}
#endif /* BIGDECIMAL_DEBUG */
- VpFree(w2);
- VpFree(w1);
+ rbd_free_struct(w2);
+ rbd_free_struct(w1);
return 1;
}
diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec
index 1feed332f6..d215757188 100644
--- a/ext/bigdecimal/bigdecimal.gemspec
+++ b/ext/bigdecimal/bigdecimal.gemspec
@@ -2,7 +2,7 @@
Gem::Specification.new do |s|
s.name = "bigdecimal"
- s.version = "3.1.2"
+ s.version = "3.1.3"
s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"]
s.email = ["mrkn@mrkn.jp"]
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
index bd1c46743e..54fed811fb 100644
--- a/ext/bigdecimal/bigdecimal.h
+++ b/ext/bigdecimal/bigdecimal.h
@@ -102,7 +102,7 @@ extern VALUE rb_cBigDecimal;
*/
#define VP_EXPORT static
-/* Exception codes */
+/* Exception mode */
#define VP_EXCEPTION_ALL ((unsigned short)0x00FF)
#define VP_EXCEPTION_INFINITY ((unsigned short)0x0001)
#define VP_EXCEPTION_NaN ((unsigned short)0x0002)
@@ -115,18 +115,36 @@ extern VALUE rb_cBigDecimal;
#define BIGDECIMAL_EXCEPTION_MODE_DEFAULT 0U
-/* Computation mode */
+/* This is used in BigDecimal#mode */
#define VP_ROUND_MODE ((unsigned short)0x0100)
-#define VP_ROUND_UP 1
-#define VP_ROUND_DOWN 2
-#define VP_ROUND_HALF_UP 3
-#define VP_ROUND_HALF_DOWN 4
-#define VP_ROUND_CEIL 5
-#define VP_ROUND_FLOOR 6
-#define VP_ROUND_HALF_EVEN 7
+
+/* Rounding mode */
+#define VP_ROUND_UP RBD_ROUND_UP
+#define VP_ROUND_DOWN RBD_ROUND_DOWN
+#define VP_ROUND_HALF_UP RBD_ROUND_HALF_UP
+#define VP_ROUND_HALF_DOWN RBD_ROUND_HALF_DOWN
+#define VP_ROUND_CEIL RBD_ROUND_CEIL
+#define VP_ROUND_FLOOR RBD_ROUND_FLOOR
+#define VP_ROUND_HALF_EVEN RBD_ROUND_HALF_EVEN
+
+enum rbd_rounding_mode {
+ RBD_ROUND_UP = 1,
+ RBD_ROUND_DOWN = 2,
+ RBD_ROUND_HALF_UP = 3,
+ RBD_ROUND_HALF_DOWN = 4,
+ RBD_ROUND_CEIL = 5,
+ RBD_ROUND_FLOOR = 6,
+ RBD_ROUND_HALF_EVEN = 7,
+
+ RBD_ROUND_DEFAULT = RBD_ROUND_HALF_UP,
+ RBD_ROUND_TRUNCATE = RBD_ROUND_DOWN,
+ RBD_ROUND_BANKER = RBD_ROUND_HALF_EVEN,
+ RBD_ROUND_CEILING = RBD_ROUND_CEIL
+};
#define BIGDECIMAL_ROUNDING_MODE_DEFAULT VP_ROUND_HALF_UP
+/* Sign flag */
#define VP_SIGN_NaN 0 /* NaN */
#define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */
#define VP_SIGN_NEGATIVE_ZERO -1 /* Negative zero */
@@ -135,6 +153,7 @@ extern VALUE rb_cBigDecimal;
#define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */
#define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */
+/* The size of fraction part array */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define FLEXIBLE_ARRAY_SIZE /* */
#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
@@ -205,9 +224,6 @@ VP_EXPORT int VpIsNegDoubleZero(double v);
#endif
VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt);
VP_EXPORT size_t VpInit(DECDIG BaseVal);
-VP_EXPORT void *VpMemAlloc(size_t mb);
-VP_EXPORT void *VpMemRealloc(void *ptr, size_t mb);
-VP_EXPORT void VpFree(Real *pv);
VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal, int strict_p, int exc);
VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw);
VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation);
@@ -215,10 +231,10 @@ VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b);
VP_EXPORT size_t VpDivd(Real *c,Real *r,Real *a,Real *b);
VP_EXPORT int VpComp(Real *a,Real *b);
VP_EXPORT ssize_t VpExponent10(Real *a);
-VP_EXPORT void VpSzMantissa(Real *a,char *psz);
-VP_EXPORT int VpToSpecialString(Real *a,char *psz,int fPlus);
-VP_EXPORT void VpToString(Real *a, char *psz, size_t fFmt, int fPlus);
-VP_EXPORT void VpToFString(Real *a, char *psz, size_t fFmt, int fPlus);
+VP_EXPORT void VpSzMantissa(Real *a, char *buf, size_t bufsize);
+VP_EXPORT int VpToSpecialString(Real *a, char *buf, size_t bufsize, int fPlus);
+VP_EXPORT void VpToString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
+VP_EXPORT void VpToFString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
VP_EXPORT int VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne);
VP_EXPORT int VpVtoD(double *d, SIGNED_VALUE *e, Real *m);
VP_EXPORT void VpDtoV(Real *m,double d);
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
index 4920374b1a..17e7905dd6 100644
--- a/ext/bigdecimal/extconf.rb
+++ b/ext/bigdecimal/extconf.rb
@@ -71,8 +71,6 @@ have_func("rb_rational_num", "ruby.h")
have_func("rb_rational_den", "ruby.h")
have_func("rb_complex_real", "ruby.h")
have_func("rb_complex_imag", "ruby.h")
-have_func("rb_array_const_ptr", "ruby.h")
-have_func("rb_sym2str", "ruby.h")
have_func("rb_opts_exception_p", "ruby.h")
have_func("rb_category_warn", "ruby.h")
have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h")
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
index cb645d2a71..ad92f7cfe6 100644
--- a/ext/bigdecimal/lib/bigdecimal/util.rb
+++ b/ext/bigdecimal/lib/bigdecimal/util.rb
@@ -33,12 +33,16 @@ class Float < Numeric
#
# Returns the value of +float+ as a BigDecimal.
# The +precision+ parameter is used to determine the number of
- # significant digits for the result (the default is Float::DIG).
+ # significant digits for the result. When +precision+ is set to +0+,
+ # the number of digits to represent the float being converted is determined
+ # automatically.
+ # The default +precision+ is +0+.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# 0.5.to_d # => 0.5e0
+ # 1.234.to_d # => 0.1234e1
# 1.234.to_d(2) # => 0.12e1
#
# See also BigDecimal::new.
diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h
index 49b7c7667f..325554b5f5 100644
--- a/ext/bigdecimal/missing.h
+++ b/ext/bigdecimal/missing.h
@@ -35,10 +35,10 @@ extern "C" {
#endif /* RB_UNUSED_VAR */
#if defined(_MSC_VER) && _MSC_VER >= 1310
-# define HAVE___ASSUME
+# define HAVE___ASSUME 1
#elif defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1300
-# define HAVE___ASSUME
+# define HAVE___ASSUME 1
#endif
#ifndef UNREACHABLE
@@ -172,45 +172,6 @@ rb_complex_imag(VALUE cmp)
}
#endif
-/* array */
-
-#ifndef FIX_CONST_VALUE_PTR
-# if defined(__fcc__) || defined(__fcc_version) || \
- defined(__FCC__) || defined(__FCC_VERSION)
-/* workaround for old version of Fujitsu C Compiler (fcc) */
-# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x))
-# else
-# define FIX_CONST_VALUE_PTR(x) (x)
-# endif
-#endif
-
-#ifndef HAVE_RB_ARRAY_CONST_PTR
-static inline const VALUE *
-rb_array_const_ptr(VALUE a)
-{
- return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ?
- RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr);
-}
-#endif
-
-#ifndef RARRAY_CONST_PTR
-# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a)
-#endif
-
-#ifndef RARRAY_AREF
-# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i])
-#endif
-
-/* symbol */
-
-#ifndef HAVE_RB_SYM2STR
-static inline VALUE
-rb_sym2str(VALUE sym)
-{
- return rb_id2str(SYM2ID(sym));
-}
-#endif
-
/* st */
#ifndef ST2FIX
diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c
index 2f760c8eb6..4578de54e4 100644
--- a/ext/coverage/coverage.c
+++ b/ext/coverage/coverage.c
@@ -24,11 +24,37 @@ static int current_mode;
static VALUE me2counter = Qnil;
/*
+ * call-seq: Coverage.supported?(mode) -> true or false
+ *
+ * Returns true if coverage measurement is supported for the given mode.
+ *
+ * The mode should be one of the following symbols:
+ * +:lines+, +:branches+, +:methods+, +:eval+.
+ *
+ * Example:
+ *
+ * Coverage.supported?(:lines) #=> true
+ * Coverage.supported?(:all) #=> false
+ */
+static VALUE
+rb_coverage_supported(VALUE self, VALUE _mode)
+{
+ ID mode = RB_SYM2ID(_mode);
+
+ return RBOOL(
+ mode == rb_intern("lines") ||
+ mode == rb_intern("branches") ||
+ mode == rb_intern("methods") ||
+ mode == rb_intern("eval")
+ );
+}
+
+/*
* call-seq:
- * Coverage.setup => nil
- * Coverage.setup(:all) => nil
- * Coverage.setup(lines: bool, branches: bool, methods: bool) => nil
- * Coverage.setup(oneshot_lines: true) => nil
+ * Coverage.setup => nil
+ * Coverage.setup(:all) => nil
+ * Coverage.setup(lines: bool, branches: bool, methods: bool, eval: bool) => nil
+ * Coverage.setup(oneshot_lines: true) => nil
*
* Set up the coverage measurement.
*
@@ -53,7 +79,7 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
mode = 0; /* compatible mode */
}
else if (opt == ID2SYM(rb_intern("all"))) {
- mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS;
+ mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS | COVERAGE_TARGET_EVAL;
}
else {
mode = 0;
@@ -71,6 +97,8 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
mode |= COVERAGE_TARGET_LINES;
mode |= COVERAGE_TARGET_ONESHOT_LINES;
}
+ if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("eval")))))
+ mode |= COVERAGE_TARGET_EVAL;
}
if (mode & COVERAGE_TARGET_METHODS) {
@@ -93,7 +121,6 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
}
-
return Qnil;
}
@@ -104,7 +131,7 @@ rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
* Start/resume the coverage measurement.
*
* Caveat: Currently, only process-global coverage measurement is supported.
- * You cannot measure per-thread covearge. If your process has multiple thread,
+ * You cannot measure per-thread coverage. If your process has multiple thread,
* using Coverage.resume/suspend to capture code coverage executed from only
* a limited code block, may yield misleading results.
*/
@@ -124,10 +151,10 @@ rb_coverage_resume(VALUE klass)
/*
* call-seq:
- * Coverage.start => nil
- * Coverage.start(:all) => nil
- * Coverage.start(lines: bool, branches: bool, methods: bool) => nil
- * Coverage.start(oneshot_lines: true) => nil
+ * Coverage.start => nil
+ * Coverage.start(:all) => nil
+ * Coverage.start(lines: bool, branches: bool, methods: bool, eval: bool) => nil
+ * Coverage.start(oneshot_lines: true) => nil
*
* Enables the coverage measurement.
* See the documentation of Coverage class in detail.
@@ -443,7 +470,7 @@ rb_coverage_running(VALUE klass)
* This feature is experimental, so these APIs may be changed in future.
*
* Caveat: Currently, only process-global coverage measurement is supported.
- * You cannot measure per-thread covearge.
+ * You cannot measure per-thread coverage.
*
* = Usage
*
@@ -589,6 +616,9 @@ void
Init_coverage(void)
{
VALUE rb_mCoverage = rb_define_module("Coverage");
+
+ rb_define_singleton_method(rb_mCoverage, "supported?", rb_coverage_supported, 1);
+
rb_define_module_function(rb_mCoverage, "setup", rb_coverage_setup, -1);
rb_define_module_function(rb_mCoverage, "start", rb_coverage_start, -1);
rb_define_module_function(rb_mCoverage, "resume", rb_coverage_resume, 0);
diff --git a/ext/coverage/depend b/ext/coverage/depend
index 57d368d3f5..e7fab16484 100644
--- a/ext/coverage/depend
+++ b/ext/coverage/depend
@@ -165,9 +165,12 @@ coverage.o: $(top_srcdir)/ccan/check_type/check_type.h
coverage.o: $(top_srcdir)/ccan/container_of/container_of.h
coverage.o: $(top_srcdir)/ccan/list/list.h
coverage.o: $(top_srcdir)/ccan/str/str.h
+coverage.o: $(top_srcdir)/constant.h
coverage.o: $(top_srcdir)/gc.h
+coverage.o: $(top_srcdir)/id_table.h
coverage.o: $(top_srcdir)/internal.h
coverage.o: $(top_srcdir)/internal/array.h
+coverage.o: $(top_srcdir)/internal/basic_operators.h
coverage.o: $(top_srcdir)/internal/compilers.h
coverage.o: $(top_srcdir)/internal/gc.h
coverage.o: $(top_srcdir)/internal/hash.h
@@ -176,12 +179,14 @@ coverage.o: $(top_srcdir)/internal/sanitizers.h
coverage.o: $(top_srcdir)/internal/serial.h
coverage.o: $(top_srcdir)/internal/static_assert.h
coverage.o: $(top_srcdir)/internal/thread.h
+coverage.o: $(top_srcdir)/internal/variable.h
coverage.o: $(top_srcdir)/internal/vm.h
coverage.o: $(top_srcdir)/internal/warnings.h
coverage.o: $(top_srcdir)/method.h
coverage.o: $(top_srcdir)/node.h
coverage.o: $(top_srcdir)/ruby_assert.h
coverage.o: $(top_srcdir)/ruby_atomic.h
+coverage.o: $(top_srcdir)/shape.h
coverage.o: $(top_srcdir)/thread_pthread.h
coverage.o: $(top_srcdir)/vm_core.h
coverage.o: $(top_srcdir)/vm_opts.h
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
index eecbf786a3..660353ebc5 100644
--- a/ext/date/date.gemspec
+++ b/ext/date/date.gemspec
@@ -10,15 +10,22 @@ Gem::Specification.new do |s|
s.summary = "A subclass of Object includes Comparable module for handling dates."
s.description = "A subclass of Object includes Comparable module for handling dates."
- s.require_path = %w{lib}
- s.files = [
- "README.md",
- "lib/date.rb", "ext/date/date_core.c", "ext/date/date_parse.c", "ext/date/date_strftime.c",
- "ext/date/date_strptime.c", "ext/date/date_tmx.h", "ext/date/extconf.rb", "ext/date/prereq.mk",
- "ext/date/zonetab.h", "ext/date/zonetab.list"
- ]
- s.extensions = "ext/date/extconf.rb"
- s.required_ruby_version = ">= 2.4.0"
+ if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby'
+ s.platform = 'java'
+ # No files shipped, no require path, no-op for now on JRuby
+ else
+ s.require_path = %w{lib}
+
+ s.files = [
+ "README.md",
+ "lib/date.rb", "ext/date/date_core.c", "ext/date/date_parse.c", "ext/date/date_strftime.c",
+ "ext/date/date_strptime.c", "ext/date/date_tmx.h", "ext/date/extconf.rb", "ext/date/prereq.mk",
+ "ext/date/zonetab.h", "ext/date/zonetab.list"
+ ]
+ s.extensions = "ext/date/extconf.rb"
+ end
+
+ s.required_ruby_version = ">= 2.6.0"
s.authors = ["Tadayoshi Funaba"]
s.email = [nil]
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 83d493c794..21367c0ddf 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -27,6 +27,10 @@ static VALUE eDateError;
static VALUE half_days_in_day, day_in_nanoseconds;
static double positive_inf, negative_inf;
+// used by deconstruct_keys
+static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday;
+static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone;
+
#define f_boolcast(x) ((x) ? Qtrue : Qfalse)
#define f_abs(x) rb_funcall(x, rb_intern("abs"), 0)
@@ -60,7 +64,8 @@ static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self);
#define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse
inline static void
-check_numeric(VALUE obj, const char* field) {
+check_numeric(VALUE obj, const char* field)
+{
if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) {
rb_raise(rb_eTypeError, "invalid %s (not numeric)", field);
}
@@ -761,6 +766,8 @@ c_valid_civil_p(int y, int m, int d, double sg,
if (m < 0)
m += 13;
+ if (m < 1 || m > 12)
+ return 0;
if (d < 0) {
if (!c_find_ldom(y, m, sg, rjd, ns))
return 0;
@@ -4377,7 +4384,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
* Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3}
*
* For other formats, see
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
+ * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
* (Unlike Date.strftime, does not support flags and width.)
*
* See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html].
@@ -4406,7 +4413,7 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass)
* Date.strptime('sat3feb01', '%a%d%b%y') # => #<Date: 2001-02-03>
*
* For other formats, see
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
+ * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
* (Unlike Date.strftime, does not support flags and width.)
*
* See argument {start}[rdoc-ref:calendars.rdoc@Argument+start].
@@ -4506,7 +4513,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
* This method recognizes many forms in +string+,
* but it is not a validator.
* For formats, see
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings]
*
* If +string+ does not specify a valid date,
* the result is unpredictable;
@@ -4541,7 +4548,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
* This method recognizes many forms in +string+,
* but it is not a validator.
* For formats, see
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings]
* If +string+ does not specify a valid date,
* the result is unpredictable;
* consider using Date._strptime instead.
@@ -4606,7 +4613,7 @@ VALUE date__jisx0301(VALUE);
* Date._iso8601(string, limit: 128) -> hash
*
* Returns a hash of values parsed from +string+, which should contain
- * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]:
+ * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]:
*
* d = Date.new(2001, 2, 3)
* s = d.iso8601 # => "2001-02-03"
@@ -4633,7 +4640,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass)
*
* Returns a new \Date object with values parsed from +string+,
* which should contain
- * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]:
+ * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]:
*
* d = Date.new(2001, 2, 3)
* s = d.iso8601 # => "2001-02-03"
@@ -4676,7 +4683,7 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
* Date._rfc3339(string, limit: 128) -> hash
*
* Returns a hash of values parsed from +string+, which should be a valid
- * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]:
+ * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
@@ -4704,7 +4711,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
*
* Returns a new \Date object with values parsed from +string+,
* which should be a valid
- * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]:
+ * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
@@ -4816,7 +4823,7 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
* Date._rfc2822(string, limit: 128) -> hash
*
* Returns a hash of values parsed from +string+, which should be a valid
- * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]:
+ * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
@@ -4846,7 +4853,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
*
* Returns a new \Date object with values parsed from +string+,
* which should be a valid
- * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]:
+ * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
@@ -4890,7 +4897,7 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
* Date._httpdate(string, limit: 128) -> hash
*
* Returns a hash of values parsed from +string+, which should be a valid
- * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]:
+ * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
@@ -4916,7 +4923,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass)
*
* Returns a new \Date object with values parsed from +string+,
* which should be a valid
- * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]:
+ * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]:
*
* d = Date.new(2001, 2, 3)
s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
@@ -4958,7 +4965,7 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
* Date._jisx0301(string, limit: 128) -> hash
*
* Returns a hash of values parsed from +string+, which should be a valid
- * {JIS X 0301 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]:
+ * {JIS X 0301 date format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.jisx0301 # => "H13.02.03"
@@ -4984,7 +4991,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
* Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date
*
* Returns a new \Date object with values parsed from +string+,
- * which should be a valid {JIS X 0301 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]:
+ * which should be a valid {JIS X 0301 format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]:
*
* d = Date.new(2001, 2, 3)
* s = d.jisx0301 # => "H13.02.03"
@@ -6971,7 +6978,7 @@ static VALUE strftimev(const char *, VALUE,
* to_s -> string
*
* Returns a string representation of the date in +self+
- * in {ISO 8601 extended date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]
+ * in {ISO 8601 extended date format}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]
* (<tt>'%Y-%m-%d'</tt>):
*
* Date.new(2001, 2, 3).to_s # => "2001-02-03"
@@ -7252,7 +7259,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
* Date.new(2001, 2, 3).strftime # => "2001-02-03"
*
* For other formats, see
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
+ * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
*
*/
static VALUE
@@ -7284,7 +7291,7 @@ strftimev(const char *fmt, VALUE self,
* asctime -> string
*
* Equivalent to #strftime with argument <tt>'%a %b %e %T %Y'</tt>
- * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers]
+ * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers]
* <tt>'%c'</tt>):
*
* Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001"
@@ -7304,7 +7311,7 @@ d_lite_asctime(VALUE self)
* iso8601 -> string
*
* Equivalent to #strftime with argument <tt>'%Y-%m-%d'</tt>
- * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers]
+ * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers]
* <tt>'%F'</tt>);
*
* Date.new(2001, 2, 3).iso8601 # => "2001-02-03"
@@ -7322,7 +7329,7 @@ d_lite_iso8601(VALUE self)
* rfc3339 -> string
*
* Equivalent to #strftime with argument <tt>'%FT%T%:z'</tt>;
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
+ * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
*
* Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00"
*
@@ -7338,7 +7345,7 @@ d_lite_rfc3339(VALUE self)
* rfc2822 -> string
*
* Equivalent to #strftime with argument <tt>'%a, %-d %b %Y %T %z'</tt>;
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
+ * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
*
* Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
*
@@ -7355,7 +7362,7 @@ d_lite_rfc2822(VALUE self)
* httpdate -> string
*
* Equivalent to #strftime with argument <tt>'%a, %d %b %Y %T GMT'</tt>;
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
+ * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
*
* Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
*
@@ -7430,6 +7437,96 @@ d_lite_jisx0301(VALUE self)
return strftimev(fmt, self, set_tmx);
}
+static VALUE
+deconstruct_keys(VALUE self, VALUE keys, int is_datetime)
+{
+ VALUE h = rb_hash_new();
+ long i;
+
+ get_d1(self);
+
+ if (NIL_P(keys)) {
+ rb_hash_aset(h, sym_year, m_real_year(dat));
+ rb_hash_aset(h, sym_month, INT2FIX(m_mon(dat)));
+ rb_hash_aset(h, sym_day, INT2FIX(m_mday(dat)));
+ rb_hash_aset(h, sym_yday, INT2FIX(m_yday(dat)));
+ rb_hash_aset(h, sym_wday, INT2FIX(m_wday(dat)));
+ if (is_datetime) {
+ rb_hash_aset(h, sym_hour, INT2FIX(m_hour(dat)));
+ rb_hash_aset(h, sym_min, INT2FIX(m_min(dat)));
+ rb_hash_aset(h, sym_sec, INT2FIX(m_sec(dat)));
+ rb_hash_aset(h, sym_sec_fraction, m_sf_in_sec(dat));
+ rb_hash_aset(h, sym_zone, m_zone(dat));
+ }
+
+ return h;
+ }
+ if (!RB_TYPE_P(keys, T_ARRAY)) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Array or nil)",
+ rb_obj_class(keys));
+
+ }
+
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+
+ if (sym_year == key) rb_hash_aset(h, key, m_real_year(dat));
+ if (sym_month == key) rb_hash_aset(h, key, INT2FIX(m_mon(dat)));
+ if (sym_day == key) rb_hash_aset(h, key, INT2FIX(m_mday(dat)));
+ if (sym_yday == key) rb_hash_aset(h, key, INT2FIX(m_yday(dat)));
+ if (sym_wday == key) rb_hash_aset(h, key, INT2FIX(m_wday(dat)));
+ if (is_datetime) {
+ if (sym_hour == key) rb_hash_aset(h, key, INT2FIX(m_hour(dat)));
+ if (sym_min == key) rb_hash_aset(h, key, INT2FIX(m_min(dat)));
+ if (sym_sec == key) rb_hash_aset(h, key, INT2FIX(m_sec(dat)));
+ if (sym_sec_fraction == key) rb_hash_aset(h, key, m_sf_in_sec(dat));
+ if (sym_zone == key) rb_hash_aset(h, key, m_zone(dat));
+ }
+ }
+ return h;
+}
+
+/*
+ * call-seq:
+ * deconstruct_keys(array_of_names_or_nil) -> hash
+ *
+ * Returns a hash of the name/value pairs, to use in pattern matching.
+ * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ * <tt>:wday</tt>, <tt>:yday</tt>.
+ *
+ * Possible usages:
+ *
+ * d = Date.new(2022, 10, 5)
+ *
+ * if d in wday: 3, day: ..7 # uses deconstruct_keys underneath
+ * puts "first Wednesday of the month"
+ * end
+ * #=> prints "first Wednesday of the month"
+ *
+ * case d
+ * in year: ...2022
+ * puts "too old"
+ * in month: ..9
+ * puts "quarter 1-3"
+ * in wday: 1..5, month:
+ * puts "working day in month #{month}"
+ * end
+ * #=> prints "working day in month 10"
+ *
+ * Note that deconstruction by pattern can also be combined with class check:
+ *
+ * if d in Date(wday: 3, day: ..7)
+ * puts "first Wednesday of the month"
+ * end
+ *
+ */
+static VALUE
+d_lite_deconstruct_keys(VALUE self, VALUE keys)
+{
+ return deconstruct_keys(self, keys, /* is_datetime=false */ 0);
+}
+
#ifndef NDEBUG
/* :nodoc: */
static VALUE
@@ -8738,6 +8835,47 @@ dt_lite_jisx0301(int argc, VALUE *argv, VALUE self)
iso8601_timediv(self, n));
}
+/*
+ * call-seq:
+ * deconstruct_keys(array_of_names_or_nil) -> hash
+ *
+ * Returns a hash of the name/value pairs, to use in pattern matching.
+ * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ * <tt>:wday</tt>, <tt>:yday</tt>, <tt>:hour</tt>, <tt>:min</tt>,
+ * <tt>:sec</tt>, <tt>:sec_fraction</tt>, <tt>:zone</tt>.
+ *
+ * Possible usages:
+ *
+ * dt = DateTime.new(2022, 10, 5, 13, 30)
+ *
+ * if d in wday: 1..5, hour: 10..18 # uses deconstruct_keys underneath
+ * puts "Working time"
+ * end
+ * #=> prints "Working time"
+ *
+ * case dt
+ * in year: ...2022
+ * puts "too old"
+ * in month: ..9
+ * puts "quarter 1-3"
+ * in wday: 1..5, month:
+ * puts "working day in month #{month}"
+ * end
+ * #=> prints "working day in month 10"
+ *
+ * Note that deconstruction by pattern can also be combined with class check:
+ *
+ * if d in DateTime(wday: 1..5, hour: 10..18, day: ..7)
+ * puts "Working time, first week of the month"
+ * end
+ *
+ */
+static VALUE
+dt_lite_deconstruct_keys(VALUE self, VALUE keys)
+{
+ return deconstruct_keys(self, keys, /* is_datetime=true */ 1);
+}
+
/* conversions */
#define f_subsec(x) rb_funcall(x, rb_intern("subsec"), 0)
@@ -9368,6 +9506,17 @@ Init_date_core(void)
id_ge_p = rb_intern_const(">=");
id_eqeq_p = rb_intern_const("==");
+ sym_year = ID2SYM(rb_intern_const("year"));
+ sym_month = ID2SYM(rb_intern_const("month"));
+ sym_yday = ID2SYM(rb_intern_const("yday"));
+ sym_wday = ID2SYM(rb_intern_const("wday"));
+ sym_day = ID2SYM(rb_intern_const("day"));
+ sym_hour = ID2SYM(rb_intern_const("hour"));
+ sym_min = ID2SYM(rb_intern_const("min"));
+ sym_sec = ID2SYM(rb_intern_const("sec"));
+ sym_sec_fraction = ID2SYM(rb_intern_const("sec_fraction"));
+ sym_zone = ID2SYM(rb_intern_const("zone"));
+
half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2));
#if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS
@@ -9392,7 +9541,7 @@ Init_date_core(void)
* calendar dates.
*
* Consider using
- * {class Time}[https://docs.ruby-lang.org/en/master/Time.html]
+ * {class Time}[rdoc-ref:Time]
* instead of class \Date if:
*
* - You need both dates and times; \Date handles only dates.
@@ -9444,7 +9593,7 @@ Init_date_core(void)
* Date.strptime('fri31dec99', '%a%d%b%y') # => #<Date: 1999-12-31>
*
* See also the specialized methods in
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings]
*
* == Argument +limit+
*
@@ -9689,6 +9838,8 @@ Init_date_core(void)
rb_define_method(cDate, "httpdate", d_lite_httpdate, 0);
rb_define_method(cDate, "jisx0301", d_lite_jisx0301, 0);
+ rb_define_method(cDate, "deconstruct_keys", d_lite_deconstruct_keys, 1);
+
#ifndef NDEBUG
rb_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0);
#endif
@@ -9899,6 +10050,8 @@ Init_date_core(void)
rb_define_method(cDateTime, "rfc3339", dt_lite_rfc3339, -1);
rb_define_method(cDateTime, "jisx0301", dt_lite_jisx0301, -1);
+ rb_define_method(cDateTime, "deconstruct_keys", dt_lite_deconstruct_keys, 1);
+
/* conversions */
rb_define_method(rb_cTime, "to_time", time_to_time, 0);
diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c
index 95274d5baa..c6f26ecb91 100644
--- a/ext/date/date_parse.c
+++ b/ext/date/date_parse.c
@@ -413,7 +413,6 @@ VALUE
date_zone_to_diff(VALUE str)
{
VALUE offset = Qnil;
- VALUE vbuf = 0;
long l = RSTRING_LEN(str);
const char *s = RSTRING_PTR(str);
@@ -439,16 +438,26 @@ date_zone_to_diff(VALUE str)
l -= w;
dst = 1;
}
+
{
+ const char *zn = s;
long sl = shrunk_size(s, l);
+ char shrunk_buff[MAX_WORD_LENGTH]; /* no terminator to be added */
+ const struct zone *z = 0;
+
+ if (sl <= 0) {
+ sl = l;
+ }
+ else if (sl <= MAX_WORD_LENGTH) {
+ char *d = shrunk_buff;
+ sl = shrink_space(d, s, l);
+ zn = d;
+ }
+
if (sl > 0 && sl <= MAX_WORD_LENGTH) {
- char *d = ALLOCV_N(char, vbuf, sl);
- l = shrink_space(d, s, l);
- s = d;
+ z = zonetab(zn, (unsigned int)sl);
}
- }
- if (l > 0 && l <= MAX_WORD_LENGTH) {
- const struct zone *z = zonetab(s, (unsigned int)l);
+
if (z) {
int d = z->offset;
if (dst)
@@ -457,6 +466,7 @@ date_zone_to_diff(VALUE str)
goto ok;
}
}
+
{
char *p;
int sign = 0;
@@ -473,27 +483,53 @@ date_zone_to_diff(VALUE str)
s++;
l--;
+#define out_of_range(v, min, max) ((v) < (min) || (max) < (v))
hour = STRTOUL(s, &p, 10);
if (*p == ':') {
+ if (out_of_range(hour, 0, 23)) return Qnil;
s = ++p;
min = STRTOUL(s, &p, 10);
+ if (out_of_range(min, 0, 59)) return Qnil;
if (*p == ':') {
s = ++p;
sec = STRTOUL(s, &p, 10);
+ if (out_of_range(sec, 0, 59)) return Qnil;
}
- goto num;
}
- if (*p == ',' || *p == '.') {
- char *e = 0;
- p++;
- min = STRTOUL(p, &e, 10) * 3600;
+ else if (*p == ',' || *p == '.') {
+ /* fractional hour */
+ size_t n;
+ int ov;
+ /* no over precision for offset; 10**-7 hour = 0.36
+ * milliseconds should be enough. */
+ const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */
+
+ if (out_of_range(hour, 0, 23)) return Qnil;
+
+ n = (s + l) - ++p;
+ if (n > max_digits) n = max_digits;
+ sec = ruby_scan_digits(p, n, 10, &n, &ov);
+ if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') {
+ /* round half to even */
+ sec++;
+ }
+ sec *= 36;
if (sign) {
hour = -hour;
- min = -min;
+ sec = -sec;
+ }
+ if (n <= 2) {
+ /* HH.nn or HH.n */
+ if (n == 1) sec *= 10;
+ offset = INT2FIX(sec + hour * 3600);
+ }
+ else {
+ VALUE denom = rb_int_positive_pow(10, (int)(n - 2));
+ offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600));
+ if (rb_rational_den(offset) == INT2FIX(1)) {
+ offset = rb_rational_num(offset);
+ }
}
- offset = rb_rational_new(INT2FIX(min),
- rb_int_positive_pow(10, (int)(e - p)));
- offset = f_add(INT2FIX(hour * 3600), offset);
goto ok;
}
else if (l > 2) {
@@ -506,18 +542,16 @@ date_zone_to_diff(VALUE str)
min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov);
if (l >= 5)
sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov);
- goto num;
}
- num:
sec += min * 60 + hour * 3600;
if (sign) sec = -sec;
offset = INT2FIX(sec);
+#undef out_of_range
}
}
}
RB_GC_GUARD(str);
ok:
- ALLOCV_END(vbuf);
return offset;
}
diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c
index 7b06a31471..f731629df1 100644
--- a/ext/date/date_strptime.c
+++ b/ext/date/date_strptime.c
@@ -10,28 +10,15 @@
static const char *day_names[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday",
- "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"
};
+static const int ABBREVIATED_DAY_NAME_LENGTH = 3;
static const char *month_names[] = {
"January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December",
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static const char *merid_names[] = {
- "am", "pm",
- "a.m.", "p.m."
-};
-
-static const char *extz_pats[] = {
- ":z",
- "::z",
- ":::z"
};
+static const int ABBREVIATED_MONTH_NAME_LENGTH = 3;
#define sizeof_array(o) (sizeof o / sizeof o[0])
@@ -75,7 +62,7 @@ num_pattern_p(const char *s)
#define NUM_PATTERN_P() num_pattern_p(&fmt[fi + 1])
static long
-read_digits(const char *s, VALUE *n, size_t width)
+read_digits(const char *s, size_t slen, VALUE *n, size_t width)
{
size_t l;
@@ -83,7 +70,7 @@ read_digits(const char *s, VALUE *n, size_t width)
return 0;
l = 0;
- while (ISDIGIT(s[l])) {
+ while (l < slen && ISDIGIT(s[l])) {
if (++l == width) break;
}
@@ -131,7 +118,7 @@ do { \
#define READ_DIGITS(n,w) \
do { \
size_t l; \
- l = read_digits(&str[si], &n, w); \
+ l = read_digits(&str[si], slen - si, &n, w); \
if (l == 0) \
fail(); \
si += l; \
@@ -161,6 +148,12 @@ do { \
VALUE date_zone_to_diff(VALUE);
+static inline int
+head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t si)
+{
+ return slen - si >= len && strncasecmp(name, &str[si], len) == 0;
+}
+
static size_t
date__strptime_internal(const char *str, size_t slen,
const char *fmt, size_t flen, VALUE hash)
@@ -168,9 +161,18 @@ date__strptime_internal(const char *str, size_t slen,
size_t si, fi;
int c;
+#define HEAD_MATCH_P(len, name) head_match_p(len, name, str, slen, si)
si = fi = 0;
while (fi < flen) {
+ if (isspace((unsigned char)fmt[fi])) {
+ while (si < slen && isspace((unsigned char)str[si]))
+ si++;
+ while (++fi < flen && isspace((unsigned char)fmt[fi]));
+ continue;
+ }
+
+ if (si >= slen) fail();
switch (fmt[fi]) {
case '%':
@@ -194,12 +196,11 @@ date__strptime_internal(const char *str, size_t slen,
{
int i;
- for (i = 0; i < (int)sizeof_array(extz_pats); i++)
- if (strncmp(extz_pats[i], &fmt[fi],
- strlen(extz_pats[i])) == 0) {
- fi += i;
- goto again;
- }
+ for (i = 1; i < 3 && fi + i < flen && fmt[fi+i] == ':'; ++i);
+ if (fmt[fi+i] == 'z') {
+ fi += i - 1;
+ goto again;
+ }
fail();
}
@@ -209,10 +210,12 @@ date__strptime_internal(const char *str, size_t slen,
int i;
for (i = 0; i < (int)sizeof_array(day_names); i++) {
- size_t l = strlen(day_names[i]);
- if (strncasecmp(day_names[i], &str[si], l) == 0) {
+ const char *day_name = day_names[i];
+ size_t l = strlen(day_name);
+ if (HEAD_MATCH_P(l, day_name) ||
+ HEAD_MATCH_P(l = ABBREVIATED_DAY_NAME_LENGTH, day_name)) {
si += l;
- set_hash("wday", INT2FIX(i % 7));
+ set_hash("wday", INT2FIX(i));
goto matched;
}
}
@@ -225,10 +228,12 @@ date__strptime_internal(const char *str, size_t slen,
int i;
for (i = 0; i < (int)sizeof_array(month_names); i++) {
- size_t l = strlen(month_names[i]);
- if (strncasecmp(month_names[i], &str[si], l) == 0) {
+ const char *month_name = month_names[i];
+ size_t l = strlen(month_name);
+ if (HEAD_MATCH_P(l, month_name) ||
+ HEAD_MATCH_P(l = ABBREVIATED_MONTH_NAME_LENGTH, month_name)) {
si += l;
- set_hash("mon", INT2FIX((i % 12) + 1));
+ set_hash("mon", INT2FIX(i + 1));
goto matched;
}
}
@@ -402,18 +407,19 @@ date__strptime_internal(const char *str, size_t slen,
case 'P':
case 'p':
+ if (slen - si < 2) fail();
{
- int i;
-
- for (i = 0; i < 4; i++) {
- size_t l = strlen(merid_names[i]);
- if (strncasecmp(merid_names[i], &str[si], l) == 0) {
- si += l;
- set_hash("_merid", INT2FIX((i % 2) == 0 ? 0 : 12));
- goto matched;
- }
+ char c = str[si];
+ const int hour = (c == 'P' || c == 'p') ? 12 : 0;
+ if (!hour && !(c == 'A' || c == 'a')) fail();
+ if ((c = str[si+1]) == '.') {
+ if (slen - si < 4 || str[si+3] != '.') fail();
+ c = str[si += 2];
}
- fail();
+ if (!(c == 'M' || c == 'm')) fail();
+ si += 2;
+ set_hash("_merid", INT2FIX(hour));
+ goto matched;
}
case 'Q':
@@ -587,7 +593,7 @@ date__strptime_internal(const char *str, size_t slen,
b = rb_backref_get();
rb_match_busy(b);
- m = f_match(pat, rb_usascii_str_new2(&str[si]));
+ m = f_match(pat, rb_usascii_str_new(&str[si], slen - si));
if (!NIL_P(m)) {
VALUE s, l, o;
@@ -619,22 +625,13 @@ date__strptime_internal(const char *str, size_t slen,
if (str[si] != '%')
fail();
si++;
- if (fi < flen)
- if (str[si] != fmt[fi])
+ if (fi < flen) {
+ if (si >= slen || str[si] != fmt[fi])
fail();
- si++;
+ si++;
+ }
goto matched;
}
- case ' ':
- case '\t':
- case '\n':
- case '\v':
- case '\f':
- case '\r':
- while (isspace((unsigned char)str[si]))
- si++;
- fi++;
- break;
default:
ordinal:
if (str[si] != fmt[fi])
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index 1899611440..a9fe3ce4b0 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -4,7 +4,7 @@
require 'date_core'
class Date
- VERSION = "3.2.3" # :nodoc:
+ VERSION = "3.3.3" # :nodoc:
# call-seq:
# infinite? -> false
diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h
index 39a435db16..7ced9e0308 100644
--- a/ext/date/zonetab.h
+++ b/ext/date/zonetab.h
@@ -36,7 +36,7 @@ struct zone {
int name;
int offset;
};
-static const struct zone *zonetab();
+static const struct zone *zonetab(register const char *str, register size_t len);
#line 9 "zonetab.list"
struct zone;
diff --git a/ext/date/zonetab.list b/ext/date/zonetab.list
index d2f902d2d5..748aec1d8a 100644
--- a/ext/date/zonetab.list
+++ b/ext/date/zonetab.list
@@ -3,7 +3,7 @@ struct zone {
int name;
int offset;
};
-static const struct zone *zonetab();
+static const struct zone *zonetab(register const char *str, register size_t len);
%}
struct zone;
diff --git a/ext/digest/digest_conf.rb b/ext/digest/digest_conf.rb
index 1b929d8732..36a7d75289 100644
--- a/ext/digest/digest_conf.rb
+++ b/ext/digest/digest_conf.rb
@@ -3,7 +3,7 @@
def digest_conf(name)
unless with_config("bundled-#{name}")
cc = with_config("common-digest")
- if cc == true or /\b#{name}\b/ =~ cc
+ if cc != false or /\b#{name}\b/ =~ cc
if File.exist?("#$srcdir/#{name}cc.h") and
have_header("CommonCrypto/CommonDigest.h")
$defs << "-D#{name.upcase}_USE_COMMONDIGEST"
diff --git a/ext/digest/lib/digest/version.rb b/ext/digest/lib/digest/version.rb
index 79e6aeee99..42fd7acf6e 100644
--- a/ext/digest/lib/digest/version.rb
+++ b/ext/digest/lib/digest/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Digest
- VERSION = "3.1.0"
+ VERSION = "3.1.1"
end
diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend
index 0353e7a40d..ea1ceec7fd 100644
--- a/ext/digest/md5/depend
+++ b/ext/digest/md5/depend
@@ -326,5 +326,6 @@ md5init.o: $(hdrdir)/ruby/subst.h
md5init.o: $(srcdir)/../defs.h
md5init.o: $(srcdir)/../digest.h
md5init.o: md5.h
+md5init.o: md5cc.h
md5init.o: md5init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend
index a4e454d214..48aaef158b 100644
--- a/ext/digest/sha1/depend
+++ b/ext/digest/sha1/depend
@@ -326,5 +326,6 @@ sha1init.o: $(hdrdir)/ruby/subst.h
sha1init.o: $(srcdir)/../defs.h
sha1init.o: $(srcdir)/../digest.h
sha1init.o: sha1.h
+sha1init.o: sha1cc.h
sha1init.o: sha1init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend
index 2fb598aa48..47a859068c 100644
--- a/ext/digest/sha2/depend
+++ b/ext/digest/sha2/depend
@@ -325,5 +325,6 @@ sha2init.o: $(hdrdir)/ruby/st.h
sha2init.o: $(hdrdir)/ruby/subst.h
sha2init.o: $(srcdir)/../digest.h
sha2init.o: sha2.h
+sha2init.o: sha2cc.h
sha2init.o: sha2init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/erb/escape/escape.c b/ext/erb/escape/escape.c
new file mode 100644
index 0000000000..67b2d1ef34
--- /dev/null
+++ b/ext/erb/escape/escape.c
@@ -0,0 +1,95 @@
+#include "ruby.h"
+#include "ruby/encoding.h"
+
+static VALUE rb_cERB, rb_mEscape, rb_cCGI;
+static ID id_escapeHTML;
+
+#define HTML_ESCAPE_MAX_LEN 6
+
+static const struct {
+ uint8_t len;
+ char str[HTML_ESCAPE_MAX_LEN+1];
+} html_escape_table[UCHAR_MAX+1] = {
+#define HTML_ESCAPE(c, str) [c] = {rb_strlen_lit(str), str}
+ HTML_ESCAPE('\'', "&#39;"),
+ HTML_ESCAPE('&', "&amp;"),
+ HTML_ESCAPE('"', "&quot;"),
+ HTML_ESCAPE('<', "&lt;"),
+ HTML_ESCAPE('>', "&gt;"),
+#undef HTML_ESCAPE
+};
+
+static inline void
+preserve_original_state(VALUE orig, VALUE dest)
+{
+ rb_enc_associate(dest, rb_enc_get(orig));
+}
+
+static inline long
+escaped_length(VALUE str)
+{
+ const long len = RSTRING_LEN(str);
+ if (len >= LONG_MAX / HTML_ESCAPE_MAX_LEN) {
+ ruby_malloc_size_overflow(len, HTML_ESCAPE_MAX_LEN);
+ }
+ return len * HTML_ESCAPE_MAX_LEN;
+}
+
+static VALUE
+optimized_escape_html(VALUE str)
+{
+ VALUE vbuf;
+ char *buf = ALLOCV_N(char, vbuf, escaped_length(str));
+ const char *cstr = RSTRING_PTR(str);
+ const char *end = cstr + RSTRING_LEN(str);
+
+ char *dest = buf;
+ while (cstr < end) {
+ const unsigned char c = *cstr++;
+ uint8_t len = html_escape_table[c].len;
+ if (len) {
+ memcpy(dest, html_escape_table[c].str, len);
+ dest += len;
+ }
+ else {
+ *dest++ = c;
+ }
+ }
+
+ VALUE escaped = str;
+ if (RSTRING_LEN(str) < (dest - buf)) {
+ escaped = rb_str_new(buf, dest - buf);
+ preserve_original_state(str, escaped);
+ }
+ ALLOCV_END(vbuf);
+ return escaped;
+}
+
+// ERB::Util.html_escape is different from CGI.escapeHTML in the following two parts:
+// * ERB::Util.html_escape converts an argument with #to_s first (only if it's not T_STRING)
+// * ERB::Util.html_escape does not allocate a new string when nothing needs to be escaped
+static VALUE
+erb_escape_html(VALUE self, VALUE str)
+{
+ if (!RB_TYPE_P(str, T_STRING)) {
+ str = rb_convert_type(str, T_STRING, "String", "to_s");
+ }
+
+ if (rb_enc_str_asciicompat_p(str)) {
+ return optimized_escape_html(str);
+ }
+ else {
+ return rb_funcall(rb_cCGI, id_escapeHTML, 1, str);
+ }
+}
+
+void
+Init_escape(void)
+{
+ rb_cERB = rb_define_class("ERB", rb_cObject);
+ rb_mEscape = rb_define_module_under(rb_cERB, "Escape");
+ rb_define_module_function(rb_mEscape, "html_escape", erb_escape_html, 1);
+
+ rb_cCGI = rb_define_class("CGI", rb_cObject);
+ id_escapeHTML = rb_intern("escapeHTML");
+}
diff --git a/ext/erb/escape/extconf.rb b/ext/erb/escape/extconf.rb
new file mode 100644
index 0000000000..c1002548ad
--- /dev/null
+++ b/ext/erb/escape/extconf.rb
@@ -0,0 +1,7 @@
+require 'mkmf'
+
+if RUBY_ENGINE == 'truffleruby'
+ File.write('Makefile', dummy_makefile($srcdir).join)
+else
+ create_makefile 'erb/escape'
+end
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 4cd941f586..6c7145b40b 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -47,12 +47,16 @@ static VALUE sGroup;
#define HAVE_UNAME 1
#endif
-#ifndef _WIN32
-char *getenv();
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
#endif
char *getlogin();
-#define RUBY_ETC_VERSION "1.4.0"
+#define RUBY_ETC_VERSION "1.4.2"
#ifdef HAVE_RB_DEPRECATE_CONSTANT
void rb_deprecate_constant(VALUE mod, const char *name);
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 40fc10ea1c..4e77a7167b 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -136,6 +136,14 @@ def extract_makefile(makefile, keep = true)
true
end
+def create_makefile(target, srcprefix = nil)
+ if $static and target.include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ super
+end
+
def extmake(target, basedir = 'ext', maybestatic = true)
FileUtils.mkpath target unless File.directory?(target)
begin
@@ -144,7 +152,7 @@ def extmake(target, basedir = 'ext', maybestatic = true)
d = target
until (d = File.dirname(d)) == '.'
if File.exist?("#{$top_srcdir}/#{basedir}/#{d}/extconf.rb")
- parent = (/^all:\s*install/ =~ IO.read("#{d}/Makefile") rescue false)
+ parent = (/^all:\s*install/ =~ File.read("#{d}/Makefile") rescue false)
break
end
end
@@ -163,8 +171,6 @@ def extmake(target, basedir = 'ext', maybestatic = true)
$mdir = target
$srcdir = File.join($top_srcdir, basedir, $mdir)
$preload = nil
- $objs = []
- $srcs = []
$extso = []
makefile = "./Makefile"
static = $static
@@ -198,7 +204,7 @@ def extmake(target, basedir = 'ext', maybestatic = true)
begin
$extconf_h = nil
ok &&= extract_makefile(makefile)
- old_objs = $objs
+ old_objs = $objs || []
old_cleanfiles = $distcleanfiles | $cleanfiles
conf = ["#{$srcdir}/makefile.rb", "#{$srcdir}/extconf.rb"].find {|f| File.exist?(f)}
if (!ok || ($extconf_h && !File.exist?($extconf_h)) ||
@@ -261,6 +267,8 @@ def extmake(target, basedir = 'ext', maybestatic = true)
unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR")
args += ["DESTDIR=" + relative_from($destdir, "../"+prefix)]
end
+ $objs ||= []
+ $srcs ||= []
if $static and ok and !$objs.empty? and !noinstall
args += ["static"]
$extlist.push [(maybestatic ? $static : false), target, $target, $preload]
@@ -447,9 +455,8 @@ if $extstatic
end
for dir in ["ext", File::join($top_srcdir, "ext")]
setup = File::join(dir, CONFIG['setup'])
- if File.file? setup
- f = open(setup)
- while line = f.gets()
+ if (f = File.stat(setup) and f.file? rescue next)
+ File.foreach(setup) do |line|
line.chomp!
line.sub!(/#.*$/, '')
next if /^\s*$/ =~ line
@@ -466,7 +473,6 @@ for dir in ["ext", File::join($top_srcdir, "ext")]
end
MTIMES << f.mtime
$setup = setup
- f.close
break
end
end unless $extstatic
@@ -536,9 +542,14 @@ extend Module.new {
def timestamp_file(name, target_prefix = nil)
if @gemname and name == '$(TARGET_SO_DIR)'
- name = "$(arch)/gems/#{@gemname}#{target_prefix}"
+ gem = true
+ name = "$(gem_platform)/$(ruby_version)/gems/#{@gemname}#{target_prefix}"
end
- super.sub(%r[/\.extout\.(?:-\.)?], '/.')
+ path = super.sub(%r[/\.extout\.(?:-\.)?], '/.')
+ if gem
+ nil while path.sub!(%r[/\.(gem_platform|ruby_version)\.-(?=\.)], '/$(\1)/')
+ end
+ path
end
def configuration(srcdir)
@@ -546,7 +557,13 @@ extend Module.new {
end
def create_makefile(*args, &block)
- return super unless @gemname
+ unless @gemname
+ if $static and (target = args.first).include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ return super
+ end
super(*args) do |conf|
conf.find do |s|
s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) {
@@ -607,7 +624,7 @@ CP_R = #{config_string('CP')} -r
gemlib = $(TARGET_TOPDIR)/gems/$(gem)/lib
gemlib:#{%{ $(gemlib)\n$(gemlib): $(gem_srcdir)/lib} if $nmake}
- $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -f -T $(gem_srcdir)/lib $(gemlib)
+ $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -q -f -T $(gem_srcdir)/lib $(gemlib)
clean-gemlib:
$(Q) $(#{@inplace ? 'NULLCMD' : 'RM_RF'}) $(gemlib)
@@ -716,6 +733,8 @@ begin
mf.puts "ECHO1 = $(V:1=@:)"
mf.puts "ECHO = $(ECHO1:0=@echo)"
mf.puts "MFLAGS = -$(MAKEFLAGS)" if $nmake
+ mf.puts "override MFLAGS := $(filter-out -j%,$(MFLAGS))" if $gnumake
+ mf.puts "ext_build_dir = #{File.dirname($command_output)}"
mf.puts
def mf.macro(name, values, max = 70)
@@ -758,6 +777,7 @@ begin
mf.macro "SUBMAKEOPTS", submakeopts
mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip]
mf.macro "NOTE_NAME", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb fail]
+ %w[RM RMDIRS RMDIR RMALL].each {|w| mf.macro w, [RbConfig::CONFIG[w]]}
mf.puts
targets = %w[all install static install-so install-rb clean distclean realclean]
targets.each do |tgt|
@@ -792,16 +812,20 @@ begin
exts.each do |d|
d = d[0..-2]
t = "#{d}#{tgt}"
- if /^(dist|real)?clean$/ =~ tgt
+ if clean = /^(dist|real)?clean$/.match(tgt)
deps = exts.select {|e|e.start_with?(d)}.map {|e|"#{e[0..-2]}#{tgt}"} - [t]
- pd = ' ' + deps.join(' ') unless deps.empty?
+ pd = [' clean-local', *deps].join(' ')
else
pext = File.dirname(d)
pd = " #{pext}/#{tgt}" if exts.include?("#{pext}/.")
end
mf.puts "#{t}:#{pd}\n\t$(Q)#{submake} $(MFLAGS) V=$(V) $(@F)"
+ if clean and clean.begin(1)
+ mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) -p $(@D)"
+ end
end
end
+ mf.puts "\n""clean-local:\n\t$(Q)$(RM) $(ext_build_dir)/*~ $(ext_build_dir)/*.bak $(ext_build_dir)/core"
mf.puts "\n""extso:\n"
mf.puts "\t@echo EXTSO=$(EXTSO)"
diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec
index 048e101aa5..09d3fc2568 100644
--- a/ext/fcntl/fcntl.gemspec
+++ b/ext/fcntl/fcntl.gemspec
@@ -3,7 +3,7 @@
Gem::Specification.new do |spec|
spec.name = "fcntl"
- spec.version = "1.0.1"
+ spec.version = "1.0.2"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index a74cc53b78..892f522a62 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -56,6 +56,8 @@ closure_memsize(const void * ptr)
const rb_data_type_t closure_data_type = {
"fiddle/closure",
{0, dealloc, closure_memsize,},
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
};
struct callback_args {
@@ -74,7 +76,7 @@ with_gvl_callback(void *ptr)
VALUE rbargs = rb_iv_get(self, "@args");
VALUE ctype = rb_iv_get(self, "@ctype");
int argc = RARRAY_LENINT(rbargs);
- VALUE params = rb_ary_hidden_new(argc);
+ VALUE params = rb_ary_tmp_new(argc);
VALUE ret;
VALUE cPointer;
int i, type;
@@ -90,7 +92,7 @@ with_gvl_callback(void *ptr)
case TYPE_INT:
rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
break;
- case -TYPE_INT:
+ case TYPE_UINT:
rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i]));
break;
case TYPE_VOIDP:
@@ -101,19 +103,19 @@ with_gvl_callback(void *ptr)
case TYPE_LONG:
rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
break;
- case -TYPE_LONG:
+ case TYPE_ULONG:
rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i]));
break;
case TYPE_CHAR:
rb_ary_push(params, INT2NUM(*(signed char *)x->args[i]));
break;
- case -TYPE_CHAR:
+ case TYPE_UCHAR:
rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i]));
break;
case TYPE_SHORT:
rb_ary_push(params, INT2NUM(*(signed short *)x->args[i]));
break;
- case -TYPE_SHORT:
+ case TYPE_USHORT:
rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
break;
case TYPE_DOUBLE:
@@ -126,7 +128,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG_LONG:
rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i]));
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
break;
#endif
@@ -149,7 +151,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG:
*(long *)x->resp = NUM2LONG(ret);
break;
- case -TYPE_LONG:
+ case TYPE_ULONG:
*(unsigned long *)x->resp = NUM2ULONG(ret);
break;
case TYPE_CHAR:
@@ -157,9 +159,9 @@ with_gvl_callback(void *ptr)
case TYPE_INT:
*(ffi_sarg *)x->resp = NUM2INT(ret);
break;
- case -TYPE_CHAR:
- case -TYPE_SHORT:
- case -TYPE_INT:
+ case TYPE_UCHAR:
+ case TYPE_USHORT:
+ case TYPE_UINT:
*(ffi_arg *)x->resp = NUM2UINT(ret);
break;
case TYPE_VOIDP:
@@ -175,7 +177,7 @@ with_gvl_callback(void *ptr)
case TYPE_LONG_LONG:
*(LONG_LONG *)x->resp = NUM2LL(ret);
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
*(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
break;
#endif
@@ -224,9 +226,27 @@ allocate(VALUE klass)
return i;
}
+static fiddle_closure *
+get_raw(VALUE self)
+{
+ fiddle_closure *closure;
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
+ if (!closure) {
+ rb_raise(rb_eArgError, "already freed: %+"PRIsVALUE, self);
+ }
+ return closure;
+}
+
+typedef struct {
+ VALUE self;
+ int argc;
+ VALUE *argv;
+} initialize_data;
+
static VALUE
-initialize(int rbargc, VALUE argv[], VALUE self)
+initialize_body(VALUE user_data)
{
+ initialize_data *data = (initialize_data *)user_data;
VALUE ret;
VALUE args;
VALUE normalized_args;
@@ -237,14 +257,14 @@ initialize(int rbargc, VALUE argv[], VALUE self)
ffi_status result;
int i, argc;
- if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
- abi = INT2NUM(FFI_DEFAULT_ABI);
+ if (2 == rb_scan_args(data->argc, data->argv, "21", &ret, &args, &abi))
+ abi = INT2NUM(FFI_DEFAULT_ABI);
Check_Type(args, T_ARRAY);
argc = RARRAY_LENINT(args);
- TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
+ TypedData_Get_Struct(data->self, fiddle_closure, &closure_data_type, cl);
cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
@@ -257,8 +277,8 @@ initialize(int rbargc, VALUE argv[], VALUE self)
cl->argv[argc] = NULL;
ret = rb_fiddle_type_ensure(ret);
- rb_iv_set(self, "@ctype", ret);
- rb_iv_set(self, "@args", normalized_args);
+ rb_iv_set(data->self, "@ctype", ret);
+ rb_iv_set(data->self, "@args", normalized_args);
cif = &cl->cif;
pcl = cl->pcl;
@@ -269,38 +289,75 @@ initialize(int rbargc, VALUE argv[], VALUE self)
rb_fiddle_int_to_ffi_type(NUM2INT(ret)),
cl->argv);
- if (FFI_OK != result)
- rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
+ if (FFI_OK != result) {
+ rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
+ }
#if USE_FFI_CLOSURE_ALLOC
result = ffi_prep_closure_loc(pcl, cif, callback,
- (void *)self, cl->code);
+ (void *)(data->self), cl->code);
#else
- result = ffi_prep_closure(pcl, cif, callback, (void *)self);
+ result = ffi_prep_closure(pcl, cif, callback, (void *)(data->self));
cl->code = (void *)pcl;
i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
if (i) {
- rb_sys_fail("mprotect");
+ rb_sys_fail("mprotect");
}
#endif
- if (FFI_OK != result)
- rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
+ if (FFI_OK != result) {
+ rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
+ }
- return self;
+ return data->self;
}
static VALUE
-to_i(VALUE self)
+initialize_rescue(VALUE user_data, VALUE exception)
{
- fiddle_closure * cl;
- void *code;
+ initialize_data *data = (initialize_data *)user_data;
+ dealloc(RTYPEDDATA_DATA(data->self));
+ RTYPEDDATA_DATA(data->self) = NULL;
+ rb_exc_raise(exception);
+ return data->self;
+}
- TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
+static VALUE
+initialize(int argc, VALUE *argv, VALUE self)
+{
+ initialize_data data;
+ data.self = self;
+ data.argc = argc;
+ data.argv = argv;
+ return rb_rescue(initialize_body, (VALUE)&data,
+ initialize_rescue, (VALUE)&data);
+}
- code = cl->code;
+static VALUE
+to_i(VALUE self)
+{
+ fiddle_closure *closure = get_raw(self);
+ return PTR2NUM(closure->code);
+}
- return PTR2NUM(code);
+static VALUE
+closure_free(VALUE self)
+{
+ fiddle_closure *closure;
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
+ if (closure) {
+ dealloc(closure);
+ RTYPEDDATA_DATA(self) = NULL;
+ }
+ return RUBY_Qnil;
+}
+
+static VALUE
+closure_freed_p(VALUE self)
+{
+ fiddle_closure *closure;
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
+ return closure ? RUBY_Qfalse : RUBY_Qtrue;
}
void
@@ -353,8 +410,24 @@ Init_fiddle_closure(void)
/*
* Document-method: to_i
*
- * Returns the memory address for this closure
+ * Returns the memory address for this closure.
*/
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
+
+ /*
+ * Document-method: free
+ *
+ * Free this closure explicitly. You can't use this closure anymore.
+ *
+ * If this closure is already freed, this does nothing.
+ */
+ rb_define_method(cFiddleClosure, "free", closure_free, 0);
+
+ /*
+ * Document-method: freed?
+ *
+ * Whether this closure was freed explicitly.
+ */
+ rb_define_method(cFiddleClosure, "freed?", closure_freed_p, 0);
}
/* vim: set noet sw=4 sts=4 */
diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c
index 6e0ce36378..3b70f7de4c 100644
--- a/ext/fiddle/conversions.c
+++ b/ext/fiddle/conversions.c
@@ -211,32 +211,32 @@ rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst)
case TYPE_CHAR:
dst->schar = (signed char)NUM2INT(*src);
break;
- case -TYPE_CHAR:
+ case TYPE_UCHAR:
dst->uchar = (unsigned char)NUM2UINT(*src);
break;
case TYPE_SHORT:
dst->sshort = (unsigned short)NUM2INT(*src);
break;
- case -TYPE_SHORT:
+ case TYPE_USHORT:
dst->sshort = (signed short)NUM2UINT(*src);
break;
case TYPE_INT:
dst->sint = NUM2INT(*src);
break;
- case -TYPE_INT:
+ case TYPE_UINT:
dst->uint = NUM2UINT(*src);
break;
case TYPE_LONG:
dst->slong = NUM2LONG(*src);
break;
- case -TYPE_LONG:
+ case TYPE_ULONG:
dst->ulong = NUM2ULONG(*src);
break;
#if HAVE_LONG_LONG
case TYPE_LONG_LONG:
dst->slong_long = NUM2LL(*src);
break;
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
dst->ulong_long = NUM2ULL(*src);
break;
#endif
@@ -283,24 +283,24 @@ rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval)
PTR2NUM((void *)retval.pointer));
case TYPE_CHAR:
return INT2NUM((signed char)retval.fffi_sarg);
- case -TYPE_CHAR:
+ case TYPE_UCHAR:
return INT2NUM((unsigned char)retval.fffi_arg);
case TYPE_SHORT:
return INT2NUM((signed short)retval.fffi_sarg);
- case -TYPE_SHORT:
+ case TYPE_USHORT:
return INT2NUM((unsigned short)retval.fffi_arg);
case TYPE_INT:
return INT2NUM((signed int)retval.fffi_sarg);
- case -TYPE_INT:
+ case TYPE_UINT:
return UINT2NUM((unsigned int)retval.fffi_arg);
case TYPE_LONG:
return LONG2NUM(retval.slong);
- case -TYPE_LONG:
+ case TYPE_ULONG:
return ULONG2NUM(retval.ulong);
#if HAVE_LONG_LONG
case TYPE_LONG_LONG:
return LL2NUM(retval.slong_long);
- case -TYPE_LONG_LONG:
+ case TYPE_ULONG_LONG:
return ULL2NUM(retval.ulong_long);
#endif
case TYPE_FLOAT:
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index 93b4f9d4fa..cf8b5223bb 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -46,7 +46,7 @@ end
libffi_version = nil
have_libffi = false
-bundle = enable_config('bundled-libffi')
+bundle = with_config("libffi-source-dir")
unless bundle
dir_config 'libffi'
@@ -67,27 +67,11 @@ unless bundle
end
unless have_libffi
- # for https://github.com/ruby/fiddle
- extlibs_rb = File.expand_path("../../bin/extlibs.rb", $srcdir)
- if bundle && File.exist?(extlibs_rb)
- require "fileutils"
- require_relative "../../bin/extlibs"
- extlibs = ExtLibs.new
- cache_dir = File.expand_path("../../tmp/.download_cache", $srcdir)
- ext_dir = File.expand_path("../../ext", $srcdir)
- Dir.glob("#{$srcdir}/libffi-*/").each{|dir| FileUtils.rm_rf(dir)}
- extlibs.run(["--cache=#{cache_dir}", ext_dir])
- end
- if bundle != false
- libffi_package_name = Dir.glob("#{$srcdir}/libffi-*/")
- .map {|n| File.basename(n)}
- .max_by {|n| n.scan(/\d+/).map(&:to_i)}
- end
- unless libffi_package_name
- raise "missing libffi. Please install libffi."
+ if bundle
+ libffi_srcdir = libffi_package_name = bundle
+ else
+ raise "missing libffi. Please install libffi or use --with-libffi-source-dir with libffi source location."
end
-
- libffi_srcdir = "#{$srcdir}/#{libffi_package_name}"
ffi_header = 'ffi.h'
libffi = Struct.new(*%I[dir srcdir builddir include lib a cflags ldflags opt arch]).new
libffi.dir = libffi_package_name
@@ -167,7 +151,7 @@ if libffi_version
libffi_version = libffi_version.gsub(/-rc\d+/, '')
libffi_version = (libffi_version.split('.').map(&:to_i) + [0,0])[0,3]
$defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % libffi_version }})
- puts "libffi_version: #{libffi_version.join('.')}"
+ warn "libffi_version: #{libffi_version.join('.')}"
end
case
@@ -226,7 +210,7 @@ types.each do |type, signed|
end
if libffi
- $LOCAL_LIBS.prepend("./#{libffi.a} ").strip! # to exts.mk
+ $LOCAL_LIBS.prepend("#{libffi.a} ").strip! # to exts.mk
$INCFLAGS.gsub!(/-I#{libffi.dir}/, '-I$(LIBFFI_DIR)')
end
create_makefile 'fiddle' do |conf|
diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs
deleted file mode 100644
index 68dac46a95..0000000000
--- a/ext/fiddle/extlibs
+++ /dev/null
@@ -1,13 +0,0 @@
-ver = 3.2.1
-pkg = libffi-$(ver)
-
-https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \
- md5:83b89587607e3eb65c70d361f13bab43 \
- sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \
- #
- win32/$(pkg)-mswin.patch -p0
-
-$(pkg)/config.guess -> /tool/config.guess
-$(pkg)/config.sub -> /tool/config.sub
-
-! chdir: $(pkg)| autoconf || exit 0
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index a8b5123269..c06cd5634a 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -164,137 +164,193 @@ Init_fiddle(void)
*/
rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError);
- /* Document-const: TYPE_VOID
+ VALUE mFiddleTypes = rb_define_module_under(mFiddle, "Types");
+
+ /* Document-const: Fiddle::Types::VOID
*
* C type - void
*/
- rb_define_const(mFiddle, "TYPE_VOID", INT2NUM(TYPE_VOID));
+ rb_define_const(mFiddleTypes, "VOID", INT2NUM(TYPE_VOID));
- /* Document-const: TYPE_VOIDP
+ /* Document-const: Fiddle::Types::VOIDP
*
* C type - void*
*/
- rb_define_const(mFiddle, "TYPE_VOIDP", INT2NUM(TYPE_VOIDP));
+ rb_define_const(mFiddleTypes, "VOIDP", INT2NUM(TYPE_VOIDP));
- /* Document-const: TYPE_CHAR
+ /* Document-const: Fiddle::Types::CHAR
*
* C type - char
*/
- rb_define_const(mFiddle, "TYPE_CHAR", INT2NUM(TYPE_CHAR));
+ rb_define_const(mFiddleTypes, "CHAR", INT2NUM(TYPE_CHAR));
- /* Document-const: TYPE_SHORT
+ /* Document-const: Fiddle::Types::UCHAR
+ *
+ * C type - unsigned char
+ */
+ rb_define_const(mFiddleTypes, "UCHAR", INT2NUM(TYPE_UCHAR));
+
+ /* Document-const: Fiddle::Types::SHORT
*
* C type - short
*/
- rb_define_const(mFiddle, "TYPE_SHORT", INT2NUM(TYPE_SHORT));
+ rb_define_const(mFiddleTypes, "SHORT", INT2NUM(TYPE_SHORT));
- /* Document-const: TYPE_INT
+ /* Document-const: Fiddle::Types::USHORT
+ *
+ * C type - unsigned short
+ */
+ rb_define_const(mFiddleTypes, "USHORT", INT2NUM(TYPE_USHORT));
+
+ /* Document-const: Fiddle::Types::INT
*
* C type - int
*/
- rb_define_const(mFiddle, "TYPE_INT", INT2NUM(TYPE_INT));
+ rb_define_const(mFiddleTypes, "INT", INT2NUM(TYPE_INT));
+
+ /* Document-const: Fiddle::Types::UINT
+ *
+ * C type - unsigned int
+ */
+ rb_define_const(mFiddleTypes, "UINT", INT2NUM(TYPE_UINT));
+
+ /* Document-const: Fiddle::Types::LONG
+ *
+ * C type - long
+ */
+ rb_define_const(mFiddleTypes, "LONG", INT2NUM(TYPE_LONG));
- /* Document-const: TYPE_LONG
+ /* Document-const: Fiddle::Types::ULONG
*
* C type - long
*/
- rb_define_const(mFiddle, "TYPE_LONG", INT2NUM(TYPE_LONG));
+ rb_define_const(mFiddleTypes, "ULONG", INT2NUM(TYPE_ULONG));
#if HAVE_LONG_LONG
- /* Document-const: TYPE_LONG_LONG
+ /* Document-const: Fiddle::Types::LONG_LONG
+ *
+ * C type - long long
+ */
+ rb_define_const(mFiddleTypes, "LONG_LONG", INT2NUM(TYPE_LONG_LONG));
+
+ /* Document-const: Fiddle::Types::ULONG_LONG
*
* C type - long long
*/
- rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
+ rb_define_const(mFiddleTypes, "ULONG_LONG", INT2NUM(TYPE_ULONG_LONG));
#endif
#ifdef TYPE_INT8_T
- /* Document-const: TYPE_INT8_T
+ /* Document-const: Fiddle::Types::INT8_T
*
* C type - int8_t
*/
- rb_define_const(mFiddle, "TYPE_INT8_T", INT2NUM(TYPE_INT8_T));
+ rb_define_const(mFiddleTypes, "INT8_T", INT2NUM(TYPE_INT8_T));
+
+ /* Document-const: Fiddle::Types::UINT8_T
+ *
+ * C type - uint8_t
+ */
+ rb_define_const(mFiddleTypes, "UINT8_T", INT2NUM(TYPE_UINT8_T));
#endif
#ifdef TYPE_INT16_T
- /* Document-const: TYPE_INT16_T
+ /* Document-const: Fiddle::Types::INT16_T
*
* C type - int16_t
*/
- rb_define_const(mFiddle, "TYPE_INT16_T", INT2NUM(TYPE_INT16_T));
+ rb_define_const(mFiddleTypes, "INT16_T", INT2NUM(TYPE_INT16_T));
+
+ /* Document-const: Fiddle::Types::UINT16_T
+ *
+ * C type - uint16_t
+ */
+ rb_define_const(mFiddleTypes, "UINT16_T", INT2NUM(TYPE_UINT16_T));
#endif
#ifdef TYPE_INT32_T
- /* Document-const: TYPE_INT32_T
+ /* Document-const: Fiddle::Types::INT32_T
*
* C type - int32_t
*/
- rb_define_const(mFiddle, "TYPE_INT32_T", INT2NUM(TYPE_INT32_T));
+ rb_define_const(mFiddleTypes, "INT32_T", INT2NUM(TYPE_INT32_T));
+
+ /* Document-const: Fiddle::Types::UINT32_T
+ *
+ * C type - uint32_t
+ */
+ rb_define_const(mFiddleTypes, "UINT32_T", INT2NUM(TYPE_UINT32_T));
#endif
#ifdef TYPE_INT64_T
- /* Document-const: TYPE_INT64_T
+ /* Document-const: Fiddle::Types::INT64_T
*
* C type - int64_t
*/
- rb_define_const(mFiddle, "TYPE_INT64_T", INT2NUM(TYPE_INT64_T));
+ rb_define_const(mFiddleTypes, "INT64_T", INT2NUM(TYPE_INT64_T));
+
+ /* Document-const: Fiddle::Types::UINT64_T
+ *
+ * C type - uint64_t
+ */
+ rb_define_const(mFiddleTypes, "UINT64_T", INT2NUM(TYPE_UINT64_T));
#endif
- /* Document-const: TYPE_FLOAT
+ /* Document-const: Fiddle::Types::FLOAT
*
* C type - float
*/
- rb_define_const(mFiddle, "TYPE_FLOAT", INT2NUM(TYPE_FLOAT));
+ rb_define_const(mFiddleTypes, "FLOAT", INT2NUM(TYPE_FLOAT));
- /* Document-const: TYPE_DOUBLE
+ /* Document-const: Fiddle::Types::DOUBLE
*
* C type - double
*/
- rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE));
+ rb_define_const(mFiddleTypes, "DOUBLE", INT2NUM(TYPE_DOUBLE));
#ifdef HAVE_FFI_PREP_CIF_VAR
- /* Document-const: TYPE_VARIADIC
+ /* Document-const: Fiddle::Types::VARIADIC
*
* C type - ...
*/
- rb_define_const(mFiddle, "TYPE_VARIADIC", INT2NUM(TYPE_VARIADIC));
+ rb_define_const(mFiddleTypes, "VARIADIC", INT2NUM(TYPE_VARIADIC));
#endif
- /* Document-const: TYPE_CONST_STRING
+ /* Document-const: Fiddle::Types::CONST_STRING
*
* C type - const char* ('\0' terminated const char*)
*/
- rb_define_const(mFiddle, "TYPE_CONST_STRING", INT2NUM(TYPE_CONST_STRING));
+ rb_define_const(mFiddleTypes, "CONST_STRING", INT2NUM(TYPE_CONST_STRING));
- /* Document-const: TYPE_SIZE_T
+ /* Document-const: Fiddle::Types::SIZE_T
*
* C type - size_t
*/
- rb_define_const(mFiddle, "TYPE_SIZE_T", INT2NUM(TYPE_SIZE_T));
+ rb_define_const(mFiddleTypes, "SIZE_T", INT2NUM(TYPE_SIZE_T));
- /* Document-const: TYPE_SSIZE_T
+ /* Document-const: Fiddle::Types::SSIZE_T
*
* C type - ssize_t
*/
- rb_define_const(mFiddle, "TYPE_SSIZE_T", INT2NUM(TYPE_SSIZE_T));
+ rb_define_const(mFiddleTypes, "SSIZE_T", INT2NUM(TYPE_SSIZE_T));
- /* Document-const: TYPE_PTRDIFF_T
+ /* Document-const: Fiddle::Types::PTRDIFF_T
*
* C type - ptrdiff_t
*/
- rb_define_const(mFiddle, "TYPE_PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T));
+ rb_define_const(mFiddleTypes, "PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T));
- /* Document-const: TYPE_INTPTR_T
+ /* Document-const: Fiddle::Types::INTPTR_T
*
* C type - intptr_t
*/
- rb_define_const(mFiddle, "TYPE_INTPTR_T", INT2NUM(TYPE_INTPTR_T));
+ rb_define_const(mFiddleTypes, "INTPTR_T", INT2NUM(TYPE_INTPTR_T));
- /* Document-const: TYPE_UINTPTR_T
+ /* Document-const: Fiddle::Types::UINTPTR_T
*
* C type - uintptr_t
*/
- rb_define_const(mFiddle, "TYPE_UINTPTR_T", INT2NUM(TYPE_UINTPTR_T));
+ rb_define_const(mFiddleTypes, "UINTPTR_T", INT2NUM(TYPE_UINTPTR_T));
/* Document-const: ALIGN_VOIDP
*
@@ -422,30 +478,60 @@ Init_fiddle(void)
*/
rb_define_const(mFiddle, "SIZEOF_CHAR", INT2NUM(sizeof(char)));
+ /* Document-const: SIZEOF_UCHAR
+ *
+ * size of a unsigned char
+ */
+ rb_define_const(mFiddle, "SIZEOF_UCHAR", INT2NUM(sizeof(unsigned char)));
+
/* Document-const: SIZEOF_SHORT
*
* size of a short
*/
rb_define_const(mFiddle, "SIZEOF_SHORT", INT2NUM(sizeof(short)));
+ /* Document-const: SIZEOF_USHORT
+ *
+ * size of a unsigned short
+ */
+ rb_define_const(mFiddle, "SIZEOF_USHORT", INT2NUM(sizeof(unsigned short)));
+
/* Document-const: SIZEOF_INT
*
* size of an int
*/
rb_define_const(mFiddle, "SIZEOF_INT", INT2NUM(sizeof(int)));
+ /* Document-const: SIZEOF_UINT
+ *
+ * size of an unsigned int
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINT", INT2NUM(sizeof(unsigned int)));
+
/* Document-const: SIZEOF_LONG
*
* size of a long
*/
rb_define_const(mFiddle, "SIZEOF_LONG", INT2NUM(sizeof(long)));
+ /* Document-const: SIZEOF_ULONG
+ *
+ * size of a unsigned long
+ */
+ rb_define_const(mFiddle, "SIZEOF_ULONG", INT2NUM(sizeof(unsigned long)));
+
#if HAVE_LONG_LONG
/* Document-const: SIZEOF_LONG_LONG
*
* size of a long long
*/
rb_define_const(mFiddle, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG)));
+
+ /* Document-const: SIZEOF_ULONG_LONG
+ *
+ * size of a unsigned long long
+ */
+ rb_define_const(mFiddle, "SIZEOF_ULONG_LONG", INT2NUM(sizeof(unsigned LONG_LONG)));
#endif
/* Document-const: SIZEOF_INT8_T
@@ -454,24 +540,48 @@ Init_fiddle(void)
*/
rb_define_const(mFiddle, "SIZEOF_INT8_T", INT2NUM(sizeof(int8_t)));
+ /* Document-const: SIZEOF_UINT8_T
+ *
+ * size of a uint8_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINT8_T", INT2NUM(sizeof(uint8_t)));
+
/* Document-const: SIZEOF_INT16_T
*
* size of a int16_t
*/
rb_define_const(mFiddle, "SIZEOF_INT16_T", INT2NUM(sizeof(int16_t)));
+ /* Document-const: SIZEOF_UINT16_T
+ *
+ * size of a uint16_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINT16_T", INT2NUM(sizeof(uint16_t)));
+
/* Document-const: SIZEOF_INT32_T
*
* size of a int32_t
*/
rb_define_const(mFiddle, "SIZEOF_INT32_T", INT2NUM(sizeof(int32_t)));
+ /* Document-const: SIZEOF_UINT32_T
+ *
+ * size of a uint32_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINT32_T", INT2NUM(sizeof(uint32_t)));
+
/* Document-const: SIZEOF_INT64_T
*
* size of a int64_t
*/
rb_define_const(mFiddle, "SIZEOF_INT64_T", INT2NUM(sizeof(int64_t)));
+ /* Document-const: SIZEOF_UINT64_T
+ *
+ * size of a uint64_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_UINT64_T", INT2NUM(sizeof(uint64_t)));
+
/* Document-const: SIZEOF_FLOAT
*
* size of a float
@@ -540,6 +650,30 @@ Init_fiddle(void)
rb_define_module_function(mFiddle, "realloc", rb_fiddle_realloc, 2);
rb_define_module_function(mFiddle, "free", rb_fiddle_free, 1);
+ /* Document-const: Qtrue
+ *
+ * The value of Qtrue
+ */
+ rb_define_const(mFiddle, "Qtrue", INT2NUM(Qtrue));
+
+ /* Document-const: Qfalse
+ *
+ * The value of Qfalse
+ */
+ rb_define_const(mFiddle, "Qfalse", INT2NUM(Qfalse));
+
+ /* Document-const: Qnil
+ *
+ * The value of Qnil
+ */
+ rb_define_const(mFiddle, "Qnil", INT2NUM(Qnil));
+
+ /* Document-const: Qundef
+ *
+ * The value of Qundef
+ */
+ rb_define_const(mFiddle, "Qundef", INT2NUM(Qundef));
+
Init_fiddle_function();
Init_fiddle_closure();
Init_fiddle_handle();
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
index a9c0ec4026..878109395b 100644
--- a/ext/fiddle/fiddle.gemspec
+++ b/ext/fiddle/fiddle.gemspec
@@ -20,15 +20,12 @@ Gem::Specification.new do |spec|
"LICENSE.txt",
"README.md",
"Rakefile",
- "bin/downloader.rb",
- "bin/extlibs.rb",
"ext/fiddle/closure.c",
"ext/fiddle/closure.h",
"ext/fiddle/conversions.c",
"ext/fiddle/conversions.h",
"ext/fiddle/depend",
"ext/fiddle/extconf.rb",
- "ext/fiddle/extlibs",
"ext/fiddle/fiddle.c",
"ext/fiddle/fiddle.h",
"ext/fiddle/function.c",
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 9de62a58cc..10eb9ceedb 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -111,11 +111,16 @@
#define TYPE_VOID 0
#define TYPE_VOIDP 1
#define TYPE_CHAR 2
+#define TYPE_UCHAR -TYPE_CHAR
#define TYPE_SHORT 3
+#define TYPE_USHORT -TYPE_SHORT
#define TYPE_INT 4
+#define TYPE_UINT -TYPE_INT
#define TYPE_LONG 5
+#define TYPE_ULONG -TYPE_LONG
#if HAVE_LONG_LONG
#define TYPE_LONG_LONG 6
+#define TYPE_ULONG_LONG -TYPE_LONG_LONG
#endif
#define TYPE_FLOAT 7
#define TYPE_DOUBLE 8
@@ -123,11 +128,18 @@
#define TYPE_CONST_STRING 10
#define TYPE_INT8_T TYPE_CHAR
+#define TYPE_UINT8_T -TYPE_INT8_T
+
#if SIZEOF_SHORT == 2
# define TYPE_INT16_T TYPE_SHORT
#elif SIZEOF_INT == 2
# define TYPE_INT16_T TYPE_INT
#endif
+
+#ifdef TYPE_INT16_T
+# define TYPE_UINT16_T -TYPE_INT16_T
+#endif
+
#if SIZEOF_SHORT == 4
# define TYPE_INT32_T TYPE_SHORT
#elif SIZEOF_INT == 4
@@ -135,6 +147,11 @@
#elif SIZEOF_LONG == 4
# define TYPE_INT32_T TYPE_LONG
#endif
+
+#ifdef TYPE_INT32_T
+#define TYPE_UINT32_T -TYPE_INT32_T
+#endif
+
#if SIZEOF_INT == 8
# define TYPE_INT64_T TYPE_INT
#elif SIZEOF_LONG == 8
@@ -143,6 +160,10 @@
# define TYPE_INT64_T TYPE_LONG_LONG
#endif
+#ifdef TYPE_INT64_T
+#define TYPE_UINT64_T -TYPE_INT64_T
+#endif
+
#ifndef TYPE_SSIZE_T
# if SIZEOF_SIZE_T == SIZEOF_INT
# define TYPE_SSIZE_T TYPE_INT
diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c
index 76b90909d3..ae8cc3a581 100644
--- a/ext/fiddle/handle.c
+++ b/ext/fiddle/handle.c
@@ -321,8 +321,10 @@ rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
return fiddle_handle_sym(RTLD_NEXT, sym);
}
-static VALUE
-fiddle_handle_sym(void *handle, VALUE symbol)
+typedef void (*fiddle_void_func)(void);
+
+static fiddle_void_func
+fiddle_handle_find_func(void *handle, VALUE symbol)
{
#if defined(HAVE_DLERROR)
const char *err;
@@ -330,13 +332,13 @@ fiddle_handle_sym(void *handle, VALUE symbol)
#else
# define CHECK_DLERROR
#endif
- void (*func)();
+ fiddle_void_func func;
const char *name = StringValueCStr(symbol);
#ifdef HAVE_DLERROR
dlerror();
#endif
- func = (void (*)())(VALUE)dlsym(handle, name);
+ func = (fiddle_void_func)(VALUE)dlsym(handle, name);
CHECK_DLERROR;
#if defined(FUNC_STDCALL)
if( !func ){
@@ -379,6 +381,53 @@ fiddle_handle_sym(void *handle, VALUE symbol)
xfree(name_n);
}
#endif
+
+ return func;
+}
+
+static VALUE
+rb_fiddle_handle_s_sym_defined(VALUE self, VALUE sym)
+{
+ fiddle_void_func func;
+
+ func = fiddle_handle_find_func(RTLD_NEXT, sym);
+
+ if( func ) {
+ return PTR2NUM(func);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+static VALUE
+rb_fiddle_handle_sym_defined(VALUE self, VALUE sym)
+{
+ struct dl_handle *fiddle_handle;
+ fiddle_void_func func;
+
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
+ if( ! fiddle_handle->open ){
+ rb_raise(rb_eFiddleDLError, "closed handle");
+ }
+
+ func = fiddle_handle_find_func(fiddle_handle->ptr, sym);
+
+ if( func ) {
+ return PTR2NUM(func);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+static VALUE
+fiddle_handle_sym(void *handle, VALUE symbol)
+{
+ fiddle_void_func func;
+
+ func = fiddle_handle_find_func(handle, symbol);
+
if( !func ){
rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
}
@@ -468,6 +517,7 @@ Init_fiddle_handle(void)
rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
+ rb_define_singleton_method(rb_cHandle, "sym_defined?", rb_fiddle_handle_s_sym_defined, 1);
rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
/* Document-const: NEXT
@@ -526,6 +576,7 @@ Init_fiddle_handle(void)
rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
+ rb_define_method(rb_cHandle, "sym_defined?", rb_fiddle_handle_sym_defined, 1);
rb_define_method(rb_cHandle, "file_name", rb_fiddle_handle_file_name, 0);
rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
index 4512989310..6137c487c6 100644
--- a/ext/fiddle/lib/fiddle.rb
+++ b/ext/fiddle/lib/fiddle.rb
@@ -58,7 +58,36 @@ module Fiddle
#
# See Fiddle::Handle.new for more.
def dlopen library
- Fiddle::Handle.new library
+ begin
+ Fiddle::Handle.new(library)
+ rescue DLError => error
+ case RUBY_PLATFORM
+ when /linux/
+ case error.message
+ when /\A(\/.+?): (?:invalid ELF header|file too short)/
+ # This may be a linker script:
+ # https://sourceware.org/binutils/docs/ld.html#Scripts
+ path = $1
+ else
+ raise
+ end
+ else
+ raise
+ end
+
+ File.open(path) do |input|
+ input.each_line do |line|
+ case line
+ when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
+ # TODO: Should we support multiple files?
+ return dlopen($1)
+ end
+ end
+ end
+
+ # Not found
+ raise
+ end
end
module_function :dlopen
@@ -67,4 +96,8 @@ module Fiddle
RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc:
RTLD_LAZY = Handle::RTLD_LAZY # :nodoc:
RTLD_NOW = Handle::RTLD_NOW # :nodoc:
+
+ Fiddle::Types.constants.each do |type|
+ const_set "TYPE_#{type}", Fiddle::Types.const_get(type)
+ end
end
diff --git a/ext/fiddle/lib/fiddle/closure.rb b/ext/fiddle/lib/fiddle/closure.rb
index c865a63c20..7e0077ea52 100644
--- a/ext/fiddle/lib/fiddle/closure.rb
+++ b/ext/fiddle/lib/fiddle/closure.rb
@@ -1,6 +1,31 @@
# frozen_string_literal: true
module Fiddle
class Closure
+ class << self
+ # Create a new closure. If a block is given, the created closure
+ # is automatically freed after the given block is executed.
+ #
+ # The all given arguments are passed to Fiddle::Closure.new. So
+ # using this method without block equals to Fiddle::Closure.new.
+ #
+ # == Example
+ #
+ # Fiddle::Closure.create(TYPE_INT, [TYPE_INT]) do |closure|
+ # # closure is freed automatically when this block is finished.
+ # end
+ def create(*args)
+ if block_given?
+ closure = new(*args)
+ begin
+ yield(closure)
+ ensure
+ closure.free
+ end
+ else
+ new(*args)
+ end
+ end
+ end
# the C type of the return of the FFI closure
attr_reader :ctype
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 93a05513c9..9a70402953 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -164,23 +164,23 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_LONG_LONG)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_LONG_LONG
+ return TYPE_ULONG_LONG
when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_LONG
when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/
- return -TYPE_LONG
+ return TYPE_ULONG
when /\A(?:signed\s+)?int(?:\s+\w+)?\z/
return TYPE_INT
when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/
- return -TYPE_INT
+ return TYPE_UINT
when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_SHORT
when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/
- return -TYPE_SHORT
+ return TYPE_USHORT
when /\A(?:signed\s+)?char(?:\s+\w+)?\z/
return TYPE_CHAR
when /\Aunsigned\s+char(?:\s+\w+)?\z/
- return -TYPE_CHAR
+ return TYPE_UCHAR
when /\Aint8_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT8_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -190,7 +190,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT8_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT8_T
+ return TYPE_UINT8_T
when /\Aint16_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT16_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -200,7 +200,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT16_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT16_T
+ return TYPE_UINT16_T
when /\Aint32_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT32_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -210,7 +210,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT32_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT32_T
+ return TYPE_UINT32_T
when /\Aint64_t(?:\s+\w+)?\z/
unless Fiddle.const_defined?(:TYPE_INT64_T)
raise(RuntimeError, "unsupported type: #{ty}")
@@ -220,7 +220,7 @@ module Fiddle
unless Fiddle.const_defined?(:TYPE_INT64_T)
raise(RuntimeError, "unsupported type: #{ty}")
end
- return -TYPE_INT64_T
+ return TYPE_UINT64_T
when /\Afloat(?:\s+\w+)?\z/
return TYPE_FLOAT
when /\Adouble(?:\s+\w+)?\z/
diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb
index 22eccedb76..545b985d50 100644
--- a/ext/fiddle/lib/fiddle/pack.rb
+++ b/ext/fiddle/lib/fiddle/pack.rb
@@ -11,24 +11,24 @@ module Fiddle
TYPE_LONG => ALIGN_LONG,
TYPE_FLOAT => ALIGN_FLOAT,
TYPE_DOUBLE => ALIGN_DOUBLE,
- -TYPE_CHAR => ALIGN_CHAR,
- -TYPE_SHORT => ALIGN_SHORT,
- -TYPE_INT => ALIGN_INT,
- -TYPE_LONG => ALIGN_LONG,
+ TYPE_UCHAR => ALIGN_CHAR,
+ TYPE_USHORT => ALIGN_SHORT,
+ TYPE_UINT => ALIGN_INT,
+ TYPE_ULONG => ALIGN_LONG,
}
PACK_MAP = {
- TYPE_VOIDP => "l!",
+ TYPE_VOIDP => "L!",
TYPE_CHAR => "c",
TYPE_SHORT => "s!",
TYPE_INT => "i!",
TYPE_LONG => "l!",
TYPE_FLOAT => "f",
TYPE_DOUBLE => "d",
- -TYPE_CHAR => "c",
- -TYPE_SHORT => "s!",
- -TYPE_INT => "i!",
- -TYPE_LONG => "l!",
+ TYPE_UCHAR => "C",
+ TYPE_USHORT => "S!",
+ TYPE_UINT => "I!",
+ TYPE_ULONG => "L!",
}
SIZE_MAP = {
@@ -39,16 +39,17 @@ module Fiddle
TYPE_LONG => SIZEOF_LONG,
TYPE_FLOAT => SIZEOF_FLOAT,
TYPE_DOUBLE => SIZEOF_DOUBLE,
- -TYPE_CHAR => SIZEOF_CHAR,
- -TYPE_SHORT => SIZEOF_SHORT,
- -TYPE_INT => SIZEOF_INT,
- -TYPE_LONG => SIZEOF_LONG,
+ TYPE_UCHAR => SIZEOF_CHAR,
+ TYPE_USHORT => SIZEOF_SHORT,
+ TYPE_UINT => SIZEOF_INT,
+ TYPE_ULONG => SIZEOF_LONG,
}
if defined?(TYPE_LONG_LONG)
- ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
- PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
- SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
- PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP
+ ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[TYPE_ULONG_LONG] = ALIGN_LONG_LONG
+ PACK_MAP[TYPE_LONG_LONG] = "q"
+ PACK_MAP[TYPE_ULONG_LONG] = "Q"
+ SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[TYPE_ULONG_LONG] = SIZEOF_LONG_LONG
+ PACK_MAP[TYPE_VOIDP] = "Q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP
end
def align(addr, align)
diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb
index db6504b650..719dc62e37 100644
--- a/ext/fiddle/lib/fiddle/version.rb
+++ b/ext/fiddle/lib/fiddle/version.rb
@@ -1,3 +1,3 @@
module Fiddle
- VERSION = "1.1.0"
+ VERSION = "1.1.1"
end
diff --git a/ext/io/console/console.c b/ext/io/console/console.c
index 4ec24178c4..21454a73fa 100644
--- a/ext/io/console/console.c
+++ b/ext/io/console/console.c
@@ -75,7 +75,7 @@ getattr(int fd, conmode *t)
#define SET_LAST_ERROR (0)
#endif
-static ID id_getc, id_console, id_close, id_min, id_time, id_intr;
+static ID id_getc, id_console, id_close;
#if ENABLE_IO_GETPASS
static ID id_gets, id_chomp_bang;
#endif
@@ -112,18 +112,34 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
}
#endif
+enum rawmode_opt_ids {
+ kwd_min,
+ kwd_time,
+ kwd_intr,
+ rawmode_opt_id_count
+};
+static ID rawmode_opt_ids[rawmode_opt_id_count];
+
typedef struct {
int vmin;
int vtime;
int intr;
} rawmode_arg_t;
+#ifndef UNDEF_P
+# define UNDEF_P(obj) ((obj) == Qundef)
+#endif
+#ifndef NIL_OR_UNDEF_P
+# define NIL_OR_UNDEF_P(obj) (NIL_P(obj) || UNDEF_P(obj))
+#endif
+
static rawmode_arg_t *
rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts)
{
int argc = *argcp;
rawmode_arg_t *optp = NULL;
VALUE vopts = Qnil;
+ VALUE optvals[rawmode_opt_id_count];
#ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
argc = rb_scan_args(argc, argv, "*:", NULL, &vopts);
#else
@@ -138,19 +154,20 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
}
#endif
rb_check_arity(argc, min_argc, max_argc);
- if (!NIL_P(vopts)) {
- VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min));
- VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time));
- VALUE intr = rb_hash_aref(vopts, ID2SYM(id_intr));
+ if (rb_get_kwargs(vopts, rawmode_opt_ids,
+ 0, rawmode_opt_id_count, optvals)) {
+ VALUE vmin = optvals[kwd_min];
+ VALUE vtime = optvals[kwd_time];
+ VALUE intr = optvals[kwd_intr];
/* default values by `stty raw` */
opts->vmin = 1;
opts->vtime = 0;
opts->intr = 0;
- if (!NIL_P(vmin)) {
+ if (!NIL_OR_UNDEF_P(vmin)) {
opts->vmin = NUM2INT(vmin);
optp = opts;
}
- if (!NIL_P(vtime)) {
+ if (!NIL_OR_UNDEF_P(vtime)) {
VALUE v10 = INT2FIX(10);
vtime = rb_funcall3(vtime, '*', 1, &v10);
opts->vtime = NUM2INT(vtime);
@@ -165,6 +182,7 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
opts->intr = 0;
optp = opts;
break;
+ case Qundef:
case Qnil:
break;
default:
@@ -1633,9 +1651,11 @@ Init_console(void)
#endif
id_console = rb_intern("console");
id_close = rb_intern("close");
- id_min = rb_intern("min");
- id_time = rb_intern("time");
- id_intr = rb_intern("intr");
+#define init_rawmode_opt_id(name) \
+ rawmode_opt_ids[kwd_##name] = rb_intern(#name)
+ init_rawmode_opt_id(min);
+ init_rawmode_opt_id(time);
+ init_rawmode_opt_id(intr);
#ifndef HAVE_RB_F_SEND
id___send__ = rb_intern("__send__");
#endif
diff --git a/ext/io/console/depend b/ext/io/console/depend
index 06ccdde70d..36747ef583 100644
--- a/ext/io/console/depend
+++ b/ext/io/console/depend
@@ -185,7 +185,7 @@ win32_vk.inc: win32_vk.list
-e 'n=$$F[1] and (n.strip!; /\AVK_/=~n) and' \
-e 'puts(%[#ifndef #{n}\n# define #{n} UNDEFINED_VK\n#endif])' \
$< && \
- gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \
+ gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \
| sed -f $(top_srcdir)/tool/gperf.sed \
) > $(@F)
diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec
index aa57f8ac52..d26a757b01 100644
--- a/ext/io/console/io-console.gemspec
+++ b/ext/io/console/io-console.gemspec
@@ -1,5 +1,5 @@
# -*- ruby -*-
-_VERSION = "0.5.11"
+_VERSION = "0.6.0"
Gem::Specification.new do |s|
s.name = "io-console"
diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc
index cbec7bef15..d15b1219fb 100644
--- a/ext/io/console/win32_vk.inc
+++ b/ext/io/console/win32_vk.inc
@@ -480,7 +480,7 @@
# define VK_OEM_CLEAR UNDEFINED_VK
#endif
/* ANSI-C code produced by gperf version 3.1 */
-/* Command-line: gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */
+/* Command-line: gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@@ -509,18 +509,17 @@
#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
#endif
-#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n)
#line 1 "win32_vk.list"
struct vktable {short ofs; unsigned short vk;};
-static const struct vktable *console_win32_vk(/*const char *, unsigned int*/);
+static const struct vktable *console_win32_vk(const char *, size_t);
#line 5 "win32_vk.list"
struct vktable;
/* maximum key range = 245, duplicates = 0 */
#ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
+static const unsigned char gperf_downcase[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
@@ -1007,368 +1006,368 @@ console_win32_vk (register const char *str, register size_t len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
#line 40 "win32_vk.list"
- {gperf_offsetof(stringpool, 12), VK_UP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, VK_UP},
#line 52 "win32_vk.list"
- {gperf_offsetof(stringpool, 13), VK_APPS},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, VK_APPS},
#line 159 "win32_vk.list"
- {gperf_offsetof(stringpool, 14), VK_CRSEL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, VK_CRSEL},
#line 34 "win32_vk.list"
- {gperf_offsetof(stringpool, 15), VK_SPACE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, VK_SPACE},
#line 95 "win32_vk.list"
- {gperf_offsetof(stringpool, 16), VK_SCROLL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, VK_SCROLL},
#line 29 "win32_vk.list"
- {gperf_offsetof(stringpool, 17), VK_ESCAPE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, VK_ESCAPE},
#line 9 "win32_vk.list"
- {gperf_offsetof(stringpool, 18), VK_CANCEL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, VK_CANCEL},
#line 32 "win32_vk.list"
- {gperf_offsetof(stringpool, 19), VK_ACCEPT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, VK_ACCEPT},
#line 66 "win32_vk.list"
- {gperf_offsetof(stringpool, 20), VK_SEPARATOR},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, VK_SEPARATOR},
#line 43 "win32_vk.list"
- {gperf_offsetof(stringpool, 21), VK_SELECT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, VK_SELECT},
#line 18 "win32_vk.list"
- {gperf_offsetof(stringpool, 22), VK_CONTROL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, VK_CONTROL},
#line 166 "win32_vk.list"
- {gperf_offsetof(stringpool, 23), VK_OEM_CLEAR},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, VK_OEM_CLEAR},
#line 145 "win32_vk.list"
- {gperf_offsetof(stringpool, 24), VK_OEM_RESET},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, VK_OEM_RESET},
#line 155 "win32_vk.list"
- {gperf_offsetof(stringpool, 25), VK_OEM_AUTO},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, VK_OEM_AUTO},
#line 151 "win32_vk.list"
- {gperf_offsetof(stringpool, 26), VK_OEM_CUSEL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, VK_OEM_CUSEL},
{-1},
#line 22 "win32_vk.list"
- {gperf_offsetof(stringpool, 28), VK_KANA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, VK_KANA},
#line 127 "win32_vk.list"
- {gperf_offsetof(stringpool, 29), VK_OEM_PLUS},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, VK_OEM_PLUS},
#line 35 "win32_vk.list"
- {gperf_offsetof(stringpool, 30), VK_PRIOR},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, VK_PRIOR},
#line 152 "win32_vk.list"
- {gperf_offsetof(stringpool, 31), VK_OEM_ATTN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, VK_OEM_ATTN},
#line 20 "win32_vk.list"
- {gperf_offsetof(stringpool, 32), VK_PAUSE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, VK_PAUSE},
#line 13 "win32_vk.list"
- {gperf_offsetof(stringpool, 33), VK_BACK},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, VK_BACK},
#line 144 "win32_vk.list"
- {gperf_offsetof(stringpool, 34), VK_PACKET},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, VK_PACKET},
#line 105 "win32_vk.list"
- {gperf_offsetof(stringpool, 35), VK_RCONTROL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, VK_RCONTROL},
#line 104 "win32_vk.list"
- {gperf_offsetof(stringpool, 36), VK_LCONTROL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, VK_LCONTROL},
#line 37 "win32_vk.list"
- {gperf_offsetof(stringpool, 37), VK_END},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VK_END},
#line 38 "win32_vk.list"
- {gperf_offsetof(stringpool, 38), VK_HOME},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, VK_HOME},
#line 44 "win32_vk.list"
- {gperf_offsetof(stringpool, 39), VK_PRINT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, VK_PRINT},
#line 94 "win32_vk.list"
- {gperf_offsetof(stringpool, 40), VK_NUMLOCK},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, VK_NUMLOCK},
#line 39 "win32_vk.list"
- {gperf_offsetof(stringpool, 41), VK_LEFT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, VK_LEFT},
#line 25 "win32_vk.list"
- {gperf_offsetof(stringpool, 42), VK_JUNJA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, VK_JUNJA},
#line 19 "win32_vk.list"
- {gperf_offsetof(stringpool, 43), VK_MENU},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, VK_MENU},
#line 150 "win32_vk.list"
- {gperf_offsetof(stringpool, 44), VK_OEM_WSCTRL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, VK_OEM_WSCTRL},
#line 156 "win32_vk.list"
- {gperf_offsetof(stringpool, 45), VK_OEM_ENLW},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, VK_OEM_ENLW},
#line 36 "win32_vk.list"
- {gperf_offsetof(stringpool, 46), VK_NEXT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, VK_NEXT},
#line 51 "win32_vk.list"
- {gperf_offsetof(stringpool, 47), VK_RWIN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VK_RWIN},
#line 50 "win32_vk.list"
- {gperf_offsetof(stringpool, 48), VK_LWIN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, VK_LWIN},
#line 21 "win32_vk.list"
- {gperf_offsetof(stringpool, 49), VK_CAPITAL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, VK_CAPITAL},
#line 49 "win32_vk.list"
- {gperf_offsetof(stringpool, 50), VK_HELP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, VK_HELP},
#line 164 "win32_vk.list"
- {gperf_offsetof(stringpool, 51), VK_NONAME},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, VK_NONAME},
#line 8 "win32_vk.list"
- {gperf_offsetof(stringpool, 52), VK_RBUTTON},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, VK_RBUTTON},
#line 7 "win32_vk.list"
- {gperf_offsetof(stringpool, 53), VK_LBUTTON},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, VK_LBUTTON},
#line 96 "win32_vk.list"
- {gperf_offsetof(stringpool, 54), VK_OEM_NEC_EQUAL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, VK_OEM_NEC_EQUAL},
{-1},
#line 47 "win32_vk.list"
- {gperf_offsetof(stringpool, 56), VK_INSERT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, VK_INSERT},
#line 27 "win32_vk.list"
- {gperf_offsetof(stringpool, 57), VK_HANJA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, VK_HANJA},
{-1}, {-1},
#line 46 "win32_vk.list"
- {gperf_offsetof(stringpool, 60), VK_SNAPSHOT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, VK_SNAPSHOT},
#line 158 "win32_vk.list"
- {gperf_offsetof(stringpool, 61), VK_ATTN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, VK_ATTN},
#line 14 "win32_vk.list"
- {gperf_offsetof(stringpool, 62), VK_TAB},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, VK_TAB},
#line 157 "win32_vk.list"
- {gperf_offsetof(stringpool, 63), VK_OEM_BACKTAB},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, VK_OEM_BACKTAB},
#line 143 "win32_vk.list"
- {gperf_offsetof(stringpool, 64), VK_ICO_CLEAR},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, VK_ICO_CLEAR},
#line 30 "win32_vk.list"
- {gperf_offsetof(stringpool, 65), VK_CONVERT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, VK_CONVERT},
#line 16 "win32_vk.list"
- {gperf_offsetof(stringpool, 66), VK_RETURN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, VK_RETURN},
#line 146 "win32_vk.list"
- {gperf_offsetof(stringpool, 67), VK_OEM_JUMP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, VK_OEM_JUMP},
{-1}, {-1}, {-1},
#line 111 "win32_vk.list"
- {gperf_offsetof(stringpool, 71), VK_BROWSER_STOP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, VK_BROWSER_STOP},
#line 26 "win32_vk.list"
- {gperf_offsetof(stringpool, 72), VK_FINAL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, VK_FINAL},
#line 163 "win32_vk.list"
- {gperf_offsetof(stringpool, 73), VK_ZOOM},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, VK_ZOOM},
#line 28 "win32_vk.list"
- {gperf_offsetof(stringpool, 74), VK_KANJI},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, VK_KANJI},
#line 48 "win32_vk.list"
- {gperf_offsetof(stringpool, 75), VK_DELETE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, VK_DELETE},
#line 128 "win32_vk.list"
- {gperf_offsetof(stringpool, 76), VK_OEM_COMMA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, VK_OEM_COMMA},
#line 67 "win32_vk.list"
- {gperf_offsetof(stringpool, 77), VK_SUBTRACT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, VK_SUBTRACT},
{-1},
#line 10 "win32_vk.list"
- {gperf_offsetof(stringpool, 79), VK_MBUTTON},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, VK_MBUTTON},
#line 78 "win32_vk.list"
- {gperf_offsetof(stringpool, 80), VK_F9},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, VK_F9},
#line 17 "win32_vk.list"
- {gperf_offsetof(stringpool, 81), VK_SHIFT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, VK_SHIFT},
#line 103 "win32_vk.list"
- {gperf_offsetof(stringpool, 82), VK_RSHIFT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, VK_RSHIFT},
#line 102 "win32_vk.list"
- {gperf_offsetof(stringpool, 83), VK_LSHIFT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, VK_LSHIFT},
#line 65 "win32_vk.list"
- {gperf_offsetof(stringpool, 84), VK_ADD},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, VK_ADD},
#line 31 "win32_vk.list"
- {gperf_offsetof(stringpool, 85), VK_NONCONVERT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, VK_NONCONVERT},
#line 160 "win32_vk.list"
- {gperf_offsetof(stringpool, 86), VK_EXSEL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, VK_EXSEL},
#line 126 "win32_vk.list"
- {gperf_offsetof(stringpool, 87), VK_OEM_1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str87, VK_OEM_1},
#line 138 "win32_vk.list"
- {gperf_offsetof(stringpool, 88), VK_OEM_AX},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, VK_OEM_AX},
#line 108 "win32_vk.list"
- {gperf_offsetof(stringpool, 89), VK_BROWSER_BACK},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89, VK_BROWSER_BACK},
#line 137 "win32_vk.list"
- {gperf_offsetof(stringpool, 90), VK_OEM_8},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90, VK_OEM_8},
#line 129 "win32_vk.list"
- {gperf_offsetof(stringpool, 91), VK_OEM_MINUS},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91, VK_OEM_MINUS},
#line 162 "win32_vk.list"
- {gperf_offsetof(stringpool, 92), VK_PLAY},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92, VK_PLAY},
#line 131 "win32_vk.list"
- {gperf_offsetof(stringpool, 93), VK_OEM_2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93, VK_OEM_2},
#line 15 "win32_vk.list"
- {gperf_offsetof(stringpool, 94), VK_CLEAR},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, VK_CLEAR},
#line 99 "win32_vk.list"
- {gperf_offsetof(stringpool, 95), VK_OEM_FJ_TOUROKU},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95, VK_OEM_FJ_TOUROKU},
#line 147 "win32_vk.list"
- {gperf_offsetof(stringpool, 96), VK_OEM_PA1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96, VK_OEM_PA1},
#line 140 "win32_vk.list"
- {gperf_offsetof(stringpool, 97), VK_ICO_HELP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, VK_ICO_HELP},
#line 112 "win32_vk.list"
- {gperf_offsetof(stringpool, 98), VK_BROWSER_SEARCH},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str98, VK_BROWSER_SEARCH},
#line 53 "win32_vk.list"
- {gperf_offsetof(stringpool, 99), VK_SLEEP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str99, VK_SLEEP},
{-1},
#line 70 "win32_vk.list"
- {gperf_offsetof(stringpool, 101), VK_F1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101, VK_F1},
#line 148 "win32_vk.list"
- {gperf_offsetof(stringpool, 102), VK_OEM_PA2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102, VK_OEM_PA2},
#line 154 "win32_vk.list"
- {gperf_offsetof(stringpool, 103), VK_OEM_COPY},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103, VK_OEM_COPY},
#line 77 "win32_vk.list"
- {gperf_offsetof(stringpool, 104), VK_F8},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, VK_F8},
#line 88 "win32_vk.list"
- {gperf_offsetof(stringpool, 105), VK_F19},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, VK_F19},
#line 41 "win32_vk.list"
- {gperf_offsetof(stringpool, 106), VK_RIGHT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106, VK_RIGHT},
#line 71 "win32_vk.list"
- {gperf_offsetof(stringpool, 107), VK_F2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, VK_F2},
#line 135 "win32_vk.list"
- {gperf_offsetof(stringpool, 108), VK_OEM_6},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, VK_OEM_6},
#line 87 "win32_vk.list"
- {gperf_offsetof(stringpool, 109), VK_F18},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, VK_F18},
{-1},
#line 117 "win32_vk.list"
- {gperf_offsetof(stringpool, 111), VK_VOLUME_UP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, VK_VOLUME_UP},
{-1}, {-1},
#line 120 "win32_vk.list"
- {gperf_offsetof(stringpool, 114), VK_MEDIA_STOP},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, VK_MEDIA_STOP},
#line 130 "win32_vk.list"
- {gperf_offsetof(stringpool, 115), VK_OEM_PERIOD},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str115, VK_OEM_PERIOD},
{-1},
#line 161 "win32_vk.list"
- {gperf_offsetof(stringpool, 117), VK_EREOF},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, VK_EREOF},
{-1}, {-1}, {-1},
#line 114 "win32_vk.list"
- {gperf_offsetof(stringpool, 121), VK_BROWSER_HOME},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, VK_BROWSER_HOME},
#line 75 "win32_vk.list"
- {gperf_offsetof(stringpool, 122), VK_F6},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122, VK_F6},
{-1},
#line 110 "win32_vk.list"
- {gperf_offsetof(stringpool, 124), VK_BROWSER_REFRESH},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124, VK_BROWSER_REFRESH},
{-1},
#line 165 "win32_vk.list"
- {gperf_offsetof(stringpool, 126), VK_PA1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, VK_PA1},
#line 142 "win32_vk.list"
- {gperf_offsetof(stringpool, 127), VK_PROCESSKEY},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127, VK_PROCESSKEY},
#line 68 "win32_vk.list"
- {gperf_offsetof(stringpool, 128), VK_DECIMAL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128, VK_DECIMAL},
#line 132 "win32_vk.list"
- {gperf_offsetof(stringpool, 129), VK_OEM_3},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129, VK_OEM_3},
#line 107 "win32_vk.list"
- {gperf_offsetof(stringpool, 130), VK_RMENU},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, VK_RMENU},
#line 106 "win32_vk.list"
- {gperf_offsetof(stringpool, 131), VK_LMENU},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, VK_LMENU},
#line 98 "win32_vk.list"
- {gperf_offsetof(stringpool, 132), VK_OEM_FJ_MASSHOU},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, VK_OEM_FJ_MASSHOU},
#line 54 "win32_vk.list"
- {gperf_offsetof(stringpool, 133), VK_NUMPAD0},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133, VK_NUMPAD0},
#line 24 "win32_vk.list"
- {gperf_offsetof(stringpool, 134), VK_HANGUL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, VK_HANGUL},
#line 63 "win32_vk.list"
- {gperf_offsetof(stringpool, 135), VK_NUMPAD9},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, VK_NUMPAD9},
#line 23 "win32_vk.list"
- {gperf_offsetof(stringpool, 136), VK_HANGEUL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str136, VK_HANGEUL},
#line 134 "win32_vk.list"
- {gperf_offsetof(stringpool, 137), VK_OEM_5},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137, VK_OEM_5},
#line 149 "win32_vk.list"
- {gperf_offsetof(stringpool, 138), VK_OEM_PA3},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str138, VK_OEM_PA3},
#line 115 "win32_vk.list"
- {gperf_offsetof(stringpool, 139), VK_VOLUME_MUTE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str139, VK_VOLUME_MUTE},
#line 133 "win32_vk.list"
- {gperf_offsetof(stringpool, 140), VK_OEM_4},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str140, VK_OEM_4},
#line 122 "win32_vk.list"
- {gperf_offsetof(stringpool, 141), VK_LAUNCH_MAIL},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str141, VK_LAUNCH_MAIL},
#line 97 "win32_vk.list"
- {gperf_offsetof(stringpool, 142), VK_OEM_FJ_JISHO},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str142, VK_OEM_FJ_JISHO},
#line 72 "win32_vk.list"
- {gperf_offsetof(stringpool, 143), VK_F3},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143, VK_F3},
#line 101 "win32_vk.list"
- {gperf_offsetof(stringpool, 144), VK_OEM_FJ_ROYA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144, VK_OEM_FJ_ROYA},
#line 100 "win32_vk.list"
- {gperf_offsetof(stringpool, 145), VK_OEM_FJ_LOYA},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, VK_OEM_FJ_LOYA},
{-1},
#line 42 "win32_vk.list"
- {gperf_offsetof(stringpool, 147), VK_DOWN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147, VK_DOWN},
{-1},
#line 153 "win32_vk.list"
- {gperf_offsetof(stringpool, 149), VK_OEM_FINISH},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149, VK_OEM_FINISH},
{-1},
#line 74 "win32_vk.list"
- {gperf_offsetof(stringpool, 151), VK_F5},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, VK_F5},
{-1},
#line 136 "win32_vk.list"
- {gperf_offsetof(stringpool, 153), VK_OEM_7},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str153, VK_OEM_7},
#line 73 "win32_vk.list"
- {gperf_offsetof(stringpool, 154), VK_F4},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, VK_F4},
#line 86 "win32_vk.list"
- {gperf_offsetof(stringpool, 155), VK_F17},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str155, VK_F17},
#line 55 "win32_vk.list"
- {gperf_offsetof(stringpool, 156), VK_NUMPAD1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str156, VK_NUMPAD1},
#line 141 "win32_vk.list"
- {gperf_offsetof(stringpool, 157), VK_ICO_00},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str157, VK_ICO_00},
{-1},
#line 62 "win32_vk.list"
- {gperf_offsetof(stringpool, 159), VK_NUMPAD8},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str159, VK_NUMPAD8},
{-1}, {-1},
#line 56 "win32_vk.list"
- {gperf_offsetof(stringpool, 162), VK_NUMPAD2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162, VK_NUMPAD2},
{-1},
#line 124 "win32_vk.list"
- {gperf_offsetof(stringpool, 164), VK_LAUNCH_APP1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, VK_LAUNCH_APP1},
#line 109 "win32_vk.list"
- {gperf_offsetof(stringpool, 165), VK_BROWSER_FORWARD},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str165, VK_BROWSER_FORWARD},
{-1},
#line 76 "win32_vk.list"
- {gperf_offsetof(stringpool, 167), VK_F7},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, VK_F7},
{-1}, {-1},
#line 125 "win32_vk.list"
- {gperf_offsetof(stringpool, 170), VK_LAUNCH_APP2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170, VK_LAUNCH_APP2},
#line 64 "win32_vk.list"
- {gperf_offsetof(stringpool, 171), VK_MULTIPLY},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171, VK_MULTIPLY},
{-1}, {-1},
#line 45 "win32_vk.list"
- {gperf_offsetof(stringpool, 174), VK_EXECUTE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, VK_EXECUTE},
{-1},
#line 113 "win32_vk.list"
- {gperf_offsetof(stringpool, 176), VK_BROWSER_FAVORITES},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176, VK_BROWSER_FAVORITES},
#line 60 "win32_vk.list"
- {gperf_offsetof(stringpool, 177), VK_NUMPAD6},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, VK_NUMPAD6},
{-1},
#line 85 "win32_vk.list"
- {gperf_offsetof(stringpool, 179), VK_F16},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179, VK_F16},
{-1}, {-1},
#line 79 "win32_vk.list"
- {gperf_offsetof(stringpool, 182), VK_F10},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182, VK_F10},
{-1}, {-1},
#line 116 "win32_vk.list"
- {gperf_offsetof(stringpool, 185), VK_VOLUME_DOWN},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, VK_VOLUME_DOWN},
{-1}, {-1},
#line 89 "win32_vk.list"
- {gperf_offsetof(stringpool, 188), VK_F20},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, VK_F20},
#line 119 "win32_vk.list"
- {gperf_offsetof(stringpool, 189), VK_MEDIA_PREV_TRACK},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, VK_MEDIA_PREV_TRACK},
{-1},
#line 33 "win32_vk.list"
- {gperf_offsetof(stringpool, 191), VK_MODECHANGE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, VK_MODECHANGE},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 83 "win32_vk.list"
- {gperf_offsetof(stringpool, 197), VK_F14},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197, VK_F14},
#line 57 "win32_vk.list"
- {gperf_offsetof(stringpool, 198), VK_NUMPAD3},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, VK_NUMPAD3},
#line 11 "win32_vk.list"
- {gperf_offsetof(stringpool, 199), VK_XBUTTON1},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199, VK_XBUTTON1},
{-1}, {-1}, {-1},
#line 93 "win32_vk.list"
- {gperf_offsetof(stringpool, 203), VK_F24},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, VK_F24},
{-1},
#line 12 "win32_vk.list"
- {gperf_offsetof(stringpool, 205), VK_XBUTTON2},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, VK_XBUTTON2},
#line 59 "win32_vk.list"
- {gperf_offsetof(stringpool, 206), VK_NUMPAD5},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206, VK_NUMPAD5},
{-1}, {-1},
#line 58 "win32_vk.list"
- {gperf_offsetof(stringpool, 209), VK_NUMPAD4},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, VK_NUMPAD4},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 121 "win32_vk.list"
- {gperf_offsetof(stringpool, 215), VK_MEDIA_PLAY_PAUSE},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215, VK_MEDIA_PLAY_PAUSE},
{-1},
#line 123 "win32_vk.list"
- {gperf_offsetof(stringpool, 217), VK_LAUNCH_MEDIA_SELECT},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, VK_LAUNCH_MEDIA_SELECT},
#line 80 "win32_vk.list"
- {gperf_offsetof(stringpool, 218), VK_F11},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, VK_F11},
{-1},
#line 139 "win32_vk.list"
- {gperf_offsetof(stringpool, 220), VK_OEM_102},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, VK_OEM_102},
#line 118 "win32_vk.list"
- {gperf_offsetof(stringpool, 221), VK_MEDIA_NEXT_TRACK},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, VK_MEDIA_NEXT_TRACK},
#line 61 "win32_vk.list"
- {gperf_offsetof(stringpool, 222), VK_NUMPAD7},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str222, VK_NUMPAD7},
{-1},
#line 90 "win32_vk.list"
- {gperf_offsetof(stringpool, 224), VK_F21},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, VK_F21},
{-1},
#line 82 "win32_vk.list"
- {gperf_offsetof(stringpool, 226), VK_F13},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str226, VK_F13},
{-1}, {-1},
#line 81 "win32_vk.list"
- {gperf_offsetof(stringpool, 229), VK_F12},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, VK_F12},
{-1}, {-1},
#line 92 "win32_vk.list"
- {gperf_offsetof(stringpool, 232), VK_F23},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232, VK_F23},
{-1}, {-1},
#line 91 "win32_vk.list"
- {gperf_offsetof(stringpool, 235), VK_F22},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, VK_F22},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 84 "win32_vk.list"
- {gperf_offsetof(stringpool, 242), VK_F15},
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242, VK_F15},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
#line 69 "win32_vk.list"
- {gperf_offsetof(stringpool, 256), VK_DIVIDE}
+ {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, VK_DIVIDE}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
diff --git a/ext/io/console/win32_vk.list b/ext/io/console/win32_vk.list
index 7909a4d1f0..5df3d6da57 100644
--- a/ext/io/console/win32_vk.list
+++ b/ext/io/console/win32_vk.list
@@ -1,6 +1,6 @@
%{
struct vktable {short ofs; unsigned short vk;};
-static const struct vktable *console_win32_vk(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
+static const struct vktable *console_win32_vk(const char *, size_t);
%}
struct vktable
%%
diff --git a/ext/io/nonblock/io-nonblock.gemspec b/ext/io/nonblock/io-nonblock.gemspec
index f81d4fda0a..d6df21a84d 100644
--- a/ext/io/nonblock/io-nonblock.gemspec
+++ b/ext/io/nonblock/io-nonblock.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "io-nonblock"
- spec.version = "0.1.1"
+ spec.version = "0.2.0"
spec.authors = ["Nobu Nakada"]
spec.email = ["nobu@ruby-lang.org"]
diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb
index eecdcce99f..c6230b7783 100644
--- a/ext/io/wait/extconf.rb
+++ b/ext/io/wait/extconf.rb
@@ -5,7 +5,7 @@ if RUBY_VERSION < "2.6"
File.write("Makefile", dummy_makefile($srcdir).join(""))
else
target = "io/wait"
- have_func("rb_io_wait")
+ have_func("rb_io_wait", "ruby/io.h")
unless macro_defined?("DOSISH", "#include <ruby.h>")
have_header(ioctl_h = "sys/ioctl.h") or ioctl_h = nil
fionread = %w[sys/ioctl.h sys/filio.h sys/socket.h].find do |h|
diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec
index 7b73d64200..ebc1f6f5c7 100644
--- a/ext/io/wait/io-wait.gemspec
+++ b/ext/io/wait/io-wait.gemspec
@@ -1,4 +1,4 @@
-_VERSION = "0.3.0.pre"
+_VERSION = "0.3.0"
Gem::Specification.new do |spec|
spec.name = "io-wait"
diff --git a/ext/json/VERSION b/ext/json/VERSION
index 097a15a2af..ec1cf33c3f 100644
--- a/ext/json/VERSION
+++ b/ext/json/VERSION
@@ -1 +1 @@
-2.6.2
+2.6.3
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index 9bedb65fa7..3d4326d836 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module JSON
# JSON version
- VERSION = '2.6.2'
+ VERSION = '2.6.3'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb
index feb586e1b4..4723a02aee 100644
--- a/ext/json/parser/extconf.rb
+++ b/ext/json/parser/extconf.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: false
require 'mkmf'
-have_func("rb_enc_raise", "ruby.h")
-have_func("rb_enc_interned_str", "ruby.h")
+have_func("rb_enc_raise", "ruby/encoding.h")
+have_func("rb_enc_interned_str", "ruby/encoding.h")
# checking if String#-@ (str_uminus) dedupes... '
begin
diff --git a/ext/nkf/nkf.gemspec b/ext/nkf/nkf.gemspec
index 2d77c71ff8..7f3bd4a4b1 100644
--- a/ext/nkf/nkf.gemspec
+++ b/ext/nkf/nkf.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "nkf"
- spec.version = "0.1.1"
+ spec.version = "0.1.2"
spec.authors = ["NARUSE Yui"]
spec.email = ["naruse@airemix.jp"]
diff --git a/ext/objspace/depend b/ext/objspace/depend
index c4da8031cc..52797664e0 100644
--- a/ext/objspace/depend
+++ b/ext/objspace/depend
@@ -158,12 +158,14 @@ object_tracing.o: $(hdrdir)/ruby/missing.h
object_tracing.o: $(hdrdir)/ruby/ruby.h
object_tracing.o: $(hdrdir)/ruby/st.h
object_tracing.o: $(hdrdir)/ruby/subst.h
+object_tracing.o: $(top_srcdir)/gc.h
object_tracing.o: $(top_srcdir)/internal.h
object_tracing.o: object_tracing.c
object_tracing.o: objspace.h
objspace.o: $(RUBY_EXTCONF_H)
objspace.o: $(arch_hdrdir)/ruby/config.h
objspace.o: $(hdrdir)/ruby/assert.h
+objspace.o: $(hdrdir)/ruby/atomic.h
objspace.o: $(hdrdir)/ruby/backward.h
objspace.o: $(hdrdir)/ruby/backward/2/assume.h
objspace.o: $(hdrdir)/ruby/backward/2/attributes.h
@@ -336,10 +338,16 @@ objspace.o: $(hdrdir)/ruby/regex.h
objspace.o: $(hdrdir)/ruby/ruby.h
objspace.o: $(hdrdir)/ruby/st.h
objspace.o: $(hdrdir)/ruby/subst.h
+objspace.o: $(hdrdir)/ruby/thread_native.h
+objspace.o: $(top_srcdir)/ccan/check_type/check_type.h
+objspace.o: $(top_srcdir)/ccan/container_of/container_of.h
+objspace.o: $(top_srcdir)/ccan/list/list.h
+objspace.o: $(top_srcdir)/ccan/str/str.h
objspace.o: $(top_srcdir)/gc.h
objspace.o: $(top_srcdir)/id_table.h
objspace.o: $(top_srcdir)/internal.h
objspace.o: $(top_srcdir)/internal/array.h
+objspace.o: $(top_srcdir)/internal/basic_operators.h
objspace.o: $(top_srcdir)/internal/class.h
objspace.o: $(top_srcdir)/internal/compilers.h
objspace.o: $(top_srcdir)/internal/gc.h
@@ -348,9 +356,17 @@ objspace.o: $(top_srcdir)/internal/imemo.h
objspace.o: $(top_srcdir)/internal/sanitizers.h
objspace.o: $(top_srcdir)/internal/serial.h
objspace.o: $(top_srcdir)/internal/static_assert.h
+objspace.o: $(top_srcdir)/internal/vm.h
objspace.o: $(top_srcdir)/internal/warnings.h
+objspace.o: $(top_srcdir)/method.h
objspace.o: $(top_srcdir)/node.h
+objspace.o: $(top_srcdir)/ruby_assert.h
+objspace.o: $(top_srcdir)/ruby_atomic.h
+objspace.o: $(top_srcdir)/shape.h
objspace.o: $(top_srcdir)/symbol.h
+objspace.o: $(top_srcdir)/thread_pthread.h
+objspace.o: $(top_srcdir)/vm_core.h
+objspace.o: $(top_srcdir)/vm_opts.h
objspace.o: objspace.c
objspace.o: {$(VPATH)}id.h
objspace_dump.o: $(RUBY_EXTCONF_H)
@@ -533,9 +549,13 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h
objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h
objspace_dump.o: $(top_srcdir)/ccan/list/list.h
objspace_dump.o: $(top_srcdir)/ccan/str/str.h
+objspace_dump.o: $(top_srcdir)/constant.h
objspace_dump.o: $(top_srcdir)/gc.h
+objspace_dump.o: $(top_srcdir)/id_table.h
objspace_dump.o: $(top_srcdir)/internal.h
objspace_dump.o: $(top_srcdir)/internal/array.h
+objspace_dump.o: $(top_srcdir)/internal/basic_operators.h
+objspace_dump.o: $(top_srcdir)/internal/class.h
objspace_dump.o: $(top_srcdir)/internal/compilers.h
objspace_dump.o: $(top_srcdir)/internal/gc.h
objspace_dump.o: $(top_srcdir)/internal/hash.h
@@ -544,12 +564,15 @@ objspace_dump.o: $(top_srcdir)/internal/sanitizers.h
objspace_dump.o: $(top_srcdir)/internal/serial.h
objspace_dump.o: $(top_srcdir)/internal/static_assert.h
objspace_dump.o: $(top_srcdir)/internal/string.h
+objspace_dump.o: $(top_srcdir)/internal/variable.h
objspace_dump.o: $(top_srcdir)/internal/vm.h
objspace_dump.o: $(top_srcdir)/internal/warnings.h
objspace_dump.o: $(top_srcdir)/method.h
objspace_dump.o: $(top_srcdir)/node.h
objspace_dump.o: $(top_srcdir)/ruby_assert.h
objspace_dump.o: $(top_srcdir)/ruby_atomic.h
+objspace_dump.o: $(top_srcdir)/shape.h
+objspace_dump.o: $(top_srcdir)/symbol.h
objspace_dump.o: $(top_srcdir)/thread_pthread.h
objspace_dump.o: $(top_srcdir)/vm_core.h
objspace_dump.o: $(top_srcdir)/vm_opts.h
diff --git a/ext/objspace/lib/objspace.rb b/ext/objspace/lib/objspace.rb
index 0298b0646c..6865fdda4c 100644
--- a/ext/objspace/lib/objspace.rb
+++ b/ext/objspace/lib/objspace.rb
@@ -6,14 +6,15 @@ module ObjectSpace
class << self
private :_dump
private :_dump_all
+ private :_dump_shapes
end
module_function
# call-seq:
- # ObjectSpace.dump(obj[, output: :string]) # => "{ ... }"
- # ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
- # ObjectSpace.dump(obj, output: :stdout) # => nil
+ # ObjectSpace.dump(obj[, output: :string]) -> "{ ... }"
+ # ObjectSpace.dump(obj, output: :file) -> #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
+ # ObjectSpace.dump(obj, output: :stdout) -> nil
#
# Dump the contents of a ruby object as JSON.
#
@@ -42,38 +43,88 @@ module ObjectSpace
end
- # call-seq:
- # ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
- # ObjectSpace.dump_all(output: :stdout) # => nil
- # ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..."
- # ObjectSpace.dump_all(output:
- # File.open('heap.json','w')) # => #<File:heap.json>
- # ObjectSpace.dump_all(output: :string,
- # since: 42) # => "{...}\n{...}\n..."
+ # call-seq:
+ # ObjectSpace.dump_all([output: :file]) -> #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
+ # ObjectSpace.dump_all(output: :stdout) -> nil
+ # ObjectSpace.dump_all(output: :string) -> "{...}\n{...}\n..."
+ # ObjectSpace.dump_all(output: File.open('heap.json','w')) -> #<File:heap.json>
+ # ObjectSpace.dump_all(output: :string, since: 42) -> "{...}\n{...}\n..."
+ #
+ # Dump the contents of the ruby heap as JSON.
+ #
+ # _full_ must be a boolean. If true all heap slots are dumped including the empty ones (T_NONE).
+ #
+ # _since_ must be a non-negative integer or +nil+.
#
- # Dump the contents of the ruby heap as JSON.
+ # If _since_ is a positive integer, only objects of that generation and
+ # newer generations are dumped. The current generation can be accessed using
+ # GC::count. Objects that were allocated without object allocation tracing enabled
+ # are ignored. See ::trace_object_allocations for more information and
+ # examples.
#
- # _since_ must be a non-negative integer or +nil+.
+ # If _since_ is omitted or is +nil+, all objects are dumped.
#
- # If _since_ is a positive integer, only objects of that generation and
- # newer generations are dumped. The current generation can be accessed using
- # GC::count.
+ # _shapes_ must be a boolean or a non-negative integer.
#
- # Objects that were allocated without object allocation tracing enabled
- # are ignored. See ::trace_object_allocations for more information and
- # examples.
+ # If _shapes_ is a positive integer, only shapes newer than the provided
+ # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>.
#
- # If _since_ is omitted or is +nil+, all objects are dumped.
+ # If _shapes_ is +false+, no shapes are dumped.
+ #
+ # To only dump objects allocated past a certain point you can combine _since_ and _shapes_:
+ # ObjectSpace.trace_object_allocations
+ # GC.start
+ # gc_generation = GC.count
+ # shape_generation = RubyVM.stat(:next_shape_id)
+ # call_method_to_instrument
+ # ObjectSpace.dump_all(since: gc_generation, shapes: shape_generation)
+ #
+ # This method is only expected to work with C Ruby.
+ # This is an experimental method and is subject to change.
+ # In particular, the function signature and output format are
+ # not guaranteed to be compatible in future versions of ruby.
+ def dump_all(output: :file, full: false, since: nil, shapes: true)
+ out = case output
+ when :file, nil
+ require 'tempfile'
+ Tempfile.create(%w(rubyheap .json))
+ when :stdout
+ STDOUT
+ when :string
+ +''
+ when IO
+ output
+ else
+ raise ArgumentError, "wrong output option: #{output.inspect}"
+ end
+
+ shapes = 0 if shapes == true
+ ret = _dump_all(out, full, since, shapes)
+ return nil if output == :stdout
+ ret
+ end
+
+ # call-seq:
+ # ObjectSpace.dump_shapes([output: :file]) -> #<File:/tmp/rubyshapes20131125-88469-laoj3v.json>
+ # ObjectSpace.dump_shapes(output: :stdout) -> nil
+ # ObjectSpace.dump_shapes(output: :string) -> "{...}\n{...}\n..."
+ # ObjectSpace.dump_shapes(output: File.open('shapes.json','w')) -> #<File:shapes.json>
+ # ObjectSpace.dump_all(output: :string, since: 42) -> "{...}\n{...}\n..."
+ #
+ # Dump the contents of the ruby shape tree as JSON.
+ #
+ # If _shapes_ is a positive integer, only shapes newer than the provided
+ # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>.
#
# This method is only expected to work with C Ruby.
# This is an experimental method and is subject to change.
# In particular, the function signature and output format are
# not guaranteed to be compatible in future versions of ruby.
- def dump_all(output: :file, full: false, since: nil)
+ def dump_shapes(output: :file, since: 0)
out = case output
when :file, nil
require 'tempfile'
- Tempfile.create(%w(rubyheap .json))
+ Tempfile.create(%w(rubyshapes .json))
when :stdout
STDOUT
when :string
@@ -84,7 +135,7 @@ module ObjectSpace
raise ArgumentError, "wrong output option: #{output.inspect}"
end
- ret = _dump_all(out, full, since)
+ ret = _dump_shapes(out, since)
return nil if output == :stdout
ret
end
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
index 0bf866a8f1..8c54d51eab 100644
--- a/ext/objspace/object_tracing.c
+++ b/ext/objspace/object_tracing.c
@@ -13,6 +13,7 @@
**********************************************************************/
+#include "gc.h"
#include "internal.h"
#include "ruby/debug.h"
#include "objspace.h"
@@ -121,6 +122,10 @@ freeobj_i(VALUE tpval, void *data)
st_data_t v;
struct allocation_info *info;
+ /* Modifying the st table can cause allocations, which can trigger GC.
+ * Since freeobj_i is called during GC, it must not trigger another GC. */
+ VALUE gc_disabled = rb_gc_disable_no_rest();
+
if (arg->keep_remains) {
if (st_lookup(arg->object_table, obj, &v)) {
info = (struct allocation_info *)v;
@@ -135,6 +140,8 @@ freeobj_i(VALUE tpval, void *data)
ruby_xfree(info);
}
}
+
+ if (gc_disabled == Qfalse) rb_gc_enable();
}
static int
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 0b1b094325..ca08604c95 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -493,6 +493,7 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_ARYPTN);
COUNT_NODE(NODE_FNDPTN);
COUNT_NODE(NODE_HSHPTN);
+ COUNT_NODE(NODE_ERROR);
#undef COUNT_NODE
case NODE_LAST: break;
}
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index 2917d49331..c3cc9a1e7b 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -13,10 +13,15 @@
**********************************************************************/
#include "gc.h"
+#include "id_table.h"
#include "internal.h"
+#include "internal/array.h"
+#include "internal/class.h"
#include "internal/hash.h"
#include "internal/string.h"
#include "internal/sanitizers.h"
+#include "symbol.h"
+#include "shape.h"
#include "node.h"
#include "objspace.h"
#include "ruby/debug.h"
@@ -41,6 +46,7 @@ struct dump_config {
unsigned int full_heap: 1;
unsigned int partial_dump;
size_t since;
+ size_t shapes_since;
unsigned long buffer_len;
char buffer[BUFFER_CAPACITY];
};
@@ -349,6 +355,20 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
}
}
+static inline void
+dump_append_id(struct dump_config *dc, ID id)
+{
+ if (is_instance_id(id)) {
+ dump_append_string_value(dc, rb_sym2str(ID2SYM(id)));
+ }
+ else {
+ dump_append(dc, "\"ID_INTERNAL(");
+ dump_append_sizet(dc, rb_id_to_serial(id));
+ dump_append(dc, ")\"");
+ }
+}
+
+
static void
dump_object(VALUE obj, struct dump_config *dc)
{
@@ -365,7 +385,11 @@ dump_object(VALUE obj, struct dump_config *dc)
dc->cur_obj = obj;
dc->cur_obj_references = 0;
- dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj);
+ if (BUILTIN_TYPE(obj) == T_NODE || BUILTIN_TYPE(obj) == T_IMEMO) {
+ dc->cur_obj_klass = 0;
+ } else {
+ dc->cur_obj_klass = RBASIC_CLASS(obj);
+ }
if (dc->partial_dump && (!ainfo || ainfo->generation < dc->since)) {
return;
@@ -381,6 +405,10 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, obj_type(obj));
dump_append(dc, "\"");
+ size_t shape_id = rb_shape_get_shape_id(obj);
+ dump_append(dc, ", \"shape_id\":");
+ dump_append_sizet(dc, shape_id);
+
dump_append(dc, ", \"slot_size\":");
dump_append_sizet(dc, dc->cur_page_slot_size);
@@ -456,7 +484,7 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_ARRAY:
dump_append(dc, ", \"length\":");
dump_append_ld(dc, RARRAY_LEN(obj));
- if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED))
+ if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_SHARED_FLAG))
dump_append(dc, ", \"shared\":true");
if (FL_TEST(obj, RARRAY_EMBED_FLAG))
dump_append(dc, ", \"embedded\":true");
@@ -470,6 +498,9 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_CLASS:
+ dump_append(dc, ", \"variation_count\":");
+ dump_append_d(dc, RCLASS_EXT(obj)->variation_count);
+
case T_MODULE:
if (rb_class_get_superclass(obj)) {
dump_append(dc, ", \"superclass\":");
@@ -514,7 +545,10 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_OBJECT:
dump_append(dc, ", \"ivars\":");
- dump_append_lu(dc, ROBJECT_NUMIV(obj));
+ dump_append_lu(dc, ROBJECT_IV_COUNT(obj));
+ if (rb_shape_obj_too_complex(obj)) {
+ dump_append(dc, ", \"too_complex_shape\":true");
+ }
break;
case T_FILE:
@@ -618,7 +652,7 @@ root_obj_i(const char *category, VALUE obj, void *data)
}
static void
-dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
+dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
dc->full_heap = 0;
@@ -644,6 +678,8 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
else {
dc->partial_dump = 0;
}
+
+ dc->shapes_since = RTEST(shapes) ? NUM2SIZET(shapes) : 0;
}
static VALUE
@@ -660,6 +696,7 @@ dump_result(struct dump_config *dc)
}
}
+/* :nodoc: */
static VALUE
objspace_dump(VALUE os, VALUE obj, VALUE output)
{
@@ -668,18 +705,87 @@ objspace_dump(VALUE os, VALUE obj, VALUE output)
dc.cur_page_slot_size = rb_gc_obj_slot_size(obj);
}
- dump_output(&dc, output, Qnil, Qnil);
+ dump_output(&dc, output, Qnil, Qnil, Qnil);
dump_object(obj, &dc);
return dump_result(&dc);
}
+static void
+shape_i(rb_shape_t *shape, void *data)
+{
+ struct dump_config *dc = (struct dump_config *)data;
+
+ size_t shape_id = rb_shape_id(shape);
+ if (shape_id < dc->shapes_since) {
+ return;
+ }
+
+ dump_append(dc, "{\"address\":");
+ dump_append_ref(dc, (VALUE)shape);
+
+ dump_append(dc, ", \"type\":\"SHAPE\", \"id\":");
+ dump_append_sizet(dc, shape_id);
+
+ if (shape->type != SHAPE_ROOT) {
+ dump_append(dc, ", \"parent_id\":");
+ dump_append_lu(dc, shape->parent_id);
+ }
+
+ dump_append(dc, ", \"depth\":");
+ dump_append_sizet(dc, rb_shape_depth(shape));
+
+ dump_append(dc, ", \"shape_type\":");
+ switch((enum shape_type)shape->type) {
+ case SHAPE_ROOT:
+ dump_append(dc, "\"ROOT\"");
+ break;
+ case SHAPE_IVAR:
+ dump_append(dc, "\"IVAR\"");
+
+ dump_append(dc, ",\"edge_name\":");
+ dump_append_id(dc, shape->edge_name);
+
+ break;
+ case SHAPE_FROZEN:
+ dump_append(dc, "\"FROZEN\"");
+ break;
+ case SHAPE_CAPACITY_CHANGE:
+ dump_append(dc, "\"CAPACITY_CHANGE\"");
+ dump_append(dc, ", \"capacity\":");
+ dump_append_sizet(dc, shape->capacity);
+ break;
+ case SHAPE_INITIAL_CAPACITY:
+ dump_append(dc, "\"INITIAL_CAPACITY\"");
+ dump_append(dc, ", \"capacity\":");
+ dump_append_sizet(dc, shape->capacity);
+ break;
+ case SHAPE_T_OBJECT:
+ dump_append(dc, "\"T_OBJECT\"");
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ dump_append(dc, "\"OBJ_TOO_COMPLEX\"");
+ break;
+ default:
+ rb_bug("[objspace] unexpected shape type");
+ }
+
+ dump_append(dc, ", \"edges\":");
+ dump_append_sizet(dc, rb_shape_edges_count(shape));
+
+ dump_append(dc, ", \"memsize\":");
+ dump_append_sizet(dc, rb_shape_memsize(shape));
+
+ dump_append(dc, "}\n");
+}
+
+/* :nodoc: */
static VALUE
-objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
+objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
struct dump_config dc = {0,};
- dump_output(&dc, output, full, since);
+ dump_output(&dc, output, full, since, shapes);
if (!dc.partial_dump || dc.since == 0) {
/* dump roots */
@@ -687,12 +793,29 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
if (dc.roots) dump_append(&dc, "]}\n");
}
+ if (RTEST(shapes)) {
+ rb_shape_each_shape(shape_i, &dc);
+ }
+
/* dump all objects */
rb_objspace_each_objects(heap_i, &dc);
return dump_result(&dc);
}
+/* :nodoc: */
+static VALUE
+objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes)
+{
+ struct dump_config dc = {0,};
+ dump_output(&dc, output, Qfalse, Qnil, shapes);
+
+ if (RTEST(shapes)) {
+ rb_shape_each_shape(shape_i, &dc);
+ }
+ return dump_result(&dc);
+}
+
void
Init_objspace_dump(VALUE rb_mObjSpace)
{
@@ -702,7 +825,8 @@ Init_objspace_dump(VALUE rb_mObjSpace)
#endif
rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2);
- rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 3);
+ rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4);
+ rb_define_module_function(rb_mObjSpace, "_dump_shapes", objspace_dump_shapes, 2);
/* force create static IDs */
rb_obj_gc_flags(rb_mObjSpace, 0, 0);
diff --git a/ext/openssl/History.md b/ext/openssl/History.md
index 479ec3b4a2..1e0df7dd87 100644
--- a/ext/openssl/History.md
+++ b/ext/openssl/History.md
@@ -1,3 +1,77 @@
+Version 3.1.0
+=============
+
+Ruby/OpenSSL 3.1 will be maintained for the lifetime of Ruby 3.2.
+
+Merged bug fixes in 2.2.3 and 3.0.2. Among the new features and changes are:
+
+Notable changes
+---------------
+
+* Add `OpenSSL::SSL::SSLContext#ciphersuites=` to allow setting TLS 1.3 cipher
+ suites.
+ [[GitHub #493]](https://github.com/ruby/openssl/pull/493)
+* Add `OpenSSL::SSL::SSLSocket#export_keying_material` for exporting keying
+ material of the session, as defined in RFC 5705.
+ [[GitHub #530]](https://github.com/ruby/openssl/pull/530)
+* Add `OpenSSL::SSL::SSLContext#keylog_cb=` for setting the TLS key logging
+ callback, which is useful for supporting NSS's SSLKEYLOGFILE debugging output.
+ [[GitHub #536]](https://github.com/ruby/openssl/pull/536)
+* Remove the default digest algorithm from `OpenSSL::OCSP::BasicResponse#sign`
+ and `OpenSSL::OCSP::Request#sign`. Omitting the 5th parameter of these
+ methods used to be equivalent of specifying SHA-1. This default value is now
+ removed and we will let the underlying OpenSSL library decide instead.
+ [[GitHub #507]](https://github.com/ruby/openssl/pull/507)
+* Add `OpenSSL::BN#mod_sqrt`.
+ [[GitHub #553]](https://github.com/ruby/openssl/pull/553)
+* Allow calling `OpenSSL::Cipher#update` with an empty string. This was
+ prohibited to workaround an ancient bug in OpenSSL.
+ [[GitHub #568]](https://github.com/ruby/openssl/pull/568)
+* Fix build on platforms without socket support, such as WASI. `OpenSSL::SSL`
+ will not be defined if OpenSSL is compiled with `OPENSSL_NO_SOCK`.
+ [[GitHub #558]](https://github.com/ruby/openssl/pull/558)
+* Improve support for recent LibreSSL versions. This includes HKDF support in
+ LibreSSL 3.6 and Ed25519 support in LibreSSL 3.7.
+
+
+Version 3.0.2
+=============
+
+Merged changes in 2.2.3. Additionally, the following issues are fixed by this
+release.
+
+Bug fixes
+---------
+
+* Fix OpenSSL::PKey::EC#check_key not working correctly on OpenSSL 3.0.
+ [[GitHub #563]](https://github.com/ruby/openssl/issues/563)
+ [[GitHub #580]](https://github.com/ruby/openssl/pull/580)
+
+
+Version 3.0.1
+=============
+
+Merged changes in 2.1.4 and 2.2.2. Additionally, the following issues are fixed
+by this release.
+
+Bug fixes
+---------
+
+* Add missing type check in OpenSSL::PKey::PKey#sign's optional parameters.
+ [[GitHub #531]](https://github.com/ruby/openssl/pull/531)
+* Work around OpenSSL 3.0's HMAC issues with a zero-length key.
+ [[GitHub #538]](https://github.com/ruby/openssl/pull/538)
+* Fix a regression in OpenSSL::PKey::DSA.generate's default of 'q' size.
+ [[GitHub #483]](https://github.com/ruby/openssl/issues/483)
+ [[GitHub #539]](https://github.com/ruby/openssl/pull/539)
+* Restore OpenSSL::PKey.read's ability to decode "openssl ecparam -genkey"
+ output when linked against OpenSSL 3.0.
+ [[GitHub #535]](https://github.com/ruby/openssl/pull/535)
+ [[GitHub #540]](https://github.com/ruby/openssl/pull/540)
+* Restore error checks in OpenSSL::PKey::EC#{to_der,to_pem}.
+ [[GitHub #541]](https://github.com/ruby/openssl/pull/541)
+
+
Version 3.0.0
=============
@@ -100,6 +174,27 @@ Notable changes
[[GitHub #342]](https://github.com/ruby/openssl/issues/342)
+Version 2.2.3
+=============
+
+Bug fixes
+---------
+
+* Fix serveral methods in OpenSSL::PKey::EC::Point attempting to raise an error
+ with an incorrect class, which would end up with a TypeError.
+ [[GitHub #570]](https://github.com/ruby/openssl/pull/570)
+* Fix OpenSSL::PKey::EC::Point#eql? and OpenSSL::PKey::EC::Group#eql?
+ incorrectly treated OpenSSL's internal errors as "not equal".
+ [[GitHub #564]](https://github.com/ruby/openssl/pull/564)
+* Fix build with LibreSSL 3.5 or later.
+
+
+Version 2.2.2
+=============
+
+Merged changes in 2.1.4.
+
+
Version 2.2.1
=============
@@ -194,6 +289,16 @@ Notable changes
[[GitHub #297]](https://github.com/ruby/openssl/pull/297)
+Version 2.1.4
+=============
+
+Bug fixes
+---------
+
+* Do not use pkg-config if --with-openssl-dir option is specified.
+ [[GitHub #486]](https://github.com/ruby/openssl/pull/486)
+
+
Version 2.1.3
=============
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index cc2b1f8ba2..bc3e4d3a21 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -25,8 +25,9 @@ Logging::message "=== OpenSSL for Ruby configurator ===\n"
if with_config("debug") or enable_config("debug")
$defs.push("-DOSSL_DEBUG")
end
+$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
-have_func("rb_io_maybe_wait") # Ruby 3.1
+have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
Logging::message "=== Checking for system dependent stuff... ===\n"
have_library("nsl", "t_open")
@@ -120,8 +121,13 @@ if is_libressl && ($mswin || $mingw)
end
Logging::message "=== Checking for OpenSSL features... ===\n"
+evp_h = "openssl/evp.h".freeze
+x509_h = "openssl/x509.h".freeze
+ts_h = "openssl/ts.h".freeze
+ssl_h = "openssl/ssl.h".freeze
+
# compile options
-have_func("RAND_egd")
+have_func("RAND_egd()", "openssl/rand.h")
engines = %w{dynamic 4758cca aep atalla chil
cswift nuron sureware ubsec padlock capi gmp gost cryptodev}
engines.each { |name|
@@ -132,65 +138,59 @@ engines.each { |name|
if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl
$defs.push("-DHAVE_OPAQUE_OPENSSL")
end
-have_func("EVP_MD_CTX_new")
-have_func("EVP_MD_CTX_free")
-have_func("EVP_MD_CTX_pkey_ctx")
-have_func("X509_STORE_get_ex_data")
-have_func("X509_STORE_set_ex_data")
-have_func("X509_STORE_get_ex_new_index")
-have_func("X509_CRL_get0_signature")
-have_func("X509_REQ_get0_signature")
-have_func("X509_REVOKED_get0_serialNumber")
-have_func("X509_REVOKED_get0_revocationDate")
-have_func("X509_get0_tbs_sigalg")
-have_func("X509_STORE_CTX_get0_untrusted")
-have_func("X509_STORE_CTX_get0_cert")
-have_func("X509_STORE_CTX_get0_chain")
-have_func("OCSP_SINGLERESP_get0_id")
-have_func("SSL_CTX_get_ciphers")
-have_func("X509_up_ref")
-have_func("X509_CRL_up_ref")
-have_func("X509_STORE_up_ref")
-have_func("SSL_SESSION_up_ref")
-have_func("EVP_PKEY_up_ref")
-have_func("SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h")
-have_func("SSL_CTX_get_security_level")
-have_func("X509_get0_notBefore")
-have_func("SSL_SESSION_get_protocol_version")
-have_func("TS_STATUS_INFO_get0_status")
-have_func("TS_STATUS_INFO_get0_text")
-have_func("TS_STATUS_INFO_get0_failure_info")
-have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", "openssl/ts.h")
-have_func("TS_VERIFY_CTX_set_store")
-have_func("TS_VERIFY_CTX_add_flags")
-have_func("TS_RESP_CTX_set_time_cb")
-have_func("EVP_PBE_scrypt")
-have_func("SSL_CTX_set_post_handshake_auth")
+have_func("EVP_MD_CTX_new()", evp_h)
+have_func("EVP_MD_CTX_free(NULL)", evp_h)
+have_func("EVP_MD_CTX_pkey_ctx(NULL)", evp_h)
+have_func("X509_STORE_get_ex_data(NULL, 0)", x509_h)
+have_func("X509_STORE_set_ex_data(NULL, 0, NULL)", x509_h)
+have_func("X509_STORE_get_ex_new_index(0, NULL, NULL, NULL, NULL)", x509_h)
+have_func("X509_CRL_get0_signature(NULL, NULL, NULL)", x509_h)
+have_func("X509_REQ_get0_signature(NULL, NULL, NULL)", x509_h)
+have_func("X509_REVOKED_get0_serialNumber(NULL)", x509_h)
+have_func("X509_REVOKED_get0_revocationDate(NULL)", x509_h)
+have_func("X509_get0_tbs_sigalg(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_untrusted(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_cert(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_chain(NULL)", x509_h)
+have_func("OCSP_SINGLERESP_get0_id(NULL)", "openssl/ocsp.h")
+have_func("SSL_CTX_get_ciphers(NULL)", ssl_h)
+have_func("X509_up_ref(NULL)", x509_h)
+have_func("X509_CRL_up_ref(NULL)", x509_h)
+have_func("X509_STORE_up_ref(NULL)", x509_h)
+have_func("SSL_SESSION_up_ref(NULL)", ssl_h)
+have_func("EVP_PKEY_up_ref(NULL)", evp_h)
+have_func("SSL_CTX_set_min_proto_version(NULL, 0)", ssl_h)
+have_func("SSL_CTX_get_security_level(NULL)", ssl_h)
+have_func("X509_get0_notBefore(NULL)", x509_h)
+have_func("SSL_SESSION_get_protocol_version(NULL)", ssl_h)
+have_func("TS_STATUS_INFO_get0_status(NULL)", ts_h)
+have_func("TS_STATUS_INFO_get0_text(NULL)", ts_h)
+have_func("TS_STATUS_INFO_get0_failure_info(NULL)", ts_h)
+have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", ts_h)
+have_func("TS_VERIFY_CTX_set_store(NULL, NULL)", ts_h)
+have_func("TS_VERIFY_CTX_add_flags(NULL, 0)", ts_h)
+have_func("TS_RESP_CTX_set_time_cb(NULL, NULL, NULL)", ts_h)
+have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h)
+have_func("SSL_CTX_set_post_handshake_auth(NULL, 0)", ssl_h)
# added in 1.1.1
-have_func("EVP_PKEY_check")
-have_func("SSL_CTX_set_ciphersuites")
+have_func("EVP_PKEY_check(NULL)", evp_h)
+have_func("EVP_PKEY_new_raw_private_key(0, NULL, (unsigned char *)\"\", 0)", evp_h)
+have_func("SSL_CTX_set_ciphersuites(NULL, \"\")", ssl_h)
# added in 3.0.0
-openssl_3 =
-have_func("SSL_set0_tmp_dh_pkey")
-have_func("ERR_get_error_all")
-have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", "openssl/ts.h")
-have_func("SSL_CTX_load_verify_file")
-have_func("BN_check_prime")
-have_func("EVP_MD_CTX_get0_md")
-have_func("EVP_MD_CTX_get_pkey_ctx")
-have_func("EVP_PKEY_eq")
-have_func("EVP_PKEY_dup")
+have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
+have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h")
+have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", ts_h)
+have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h)
+have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h")
+have_func("EVP_MD_CTX_get0_md(NULL)", evp_h)
+have_func("EVP_MD_CTX_get_pkey_ctx(NULL)", evp_h)
+have_func("EVP_PKEY_eq(NULL, NULL)", evp_h)
+have_func("EVP_PKEY_dup(NULL)", evp_h)
Logging::message "=== Checking done. ===\n"
-if openssl_3
- if $warnflags&.sub!(/-W\K(?=deprecated-declarations)/, 'no-')
- $warnflags << " -Wno-incompatible-pointer-types-discards-qualifiers"
- end
-end
-
create_header
create_makefile("openssl")
Logging::message "Done.\n"
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
index c3e0629091..0414658a10 100644
--- a/ext/openssl/lib/openssl/pkey.rb
+++ b/ext/openssl/lib/openssl/pkey.rb
@@ -167,8 +167,16 @@ module OpenSSL::PKey
# +size+::
# The desired key size in bits.
def generate(size, &blk)
+ # FIPS 186-4 specifies four (L,N) pairs: (1024,160), (2048,224),
+ # (2048,256), and (3072,256).
+ #
+ # q size is derived here with compatibility with
+ # DSA_generator_parameters_ex() which previous versions of ruby/openssl
+ # used to call.
+ qsize = size >= 2048 ? 256 : 160
dsaparams = OpenSSL::PKey.generate_parameters("DSA", {
"dsa_paramgen_bits" => size,
+ "dsa_paramgen_q_bits" => qsize,
}, &blk)
OpenSSL::PKey.generate_key(dsaparams)
end
@@ -355,7 +363,8 @@ module OpenSSL::PKey
# rsa.private_encrypt(string, padding) -> String
#
# Encrypt +string+ with the private key. +padding+ defaults to
- # PKCS1_PADDING. The encrypted string output can be decrypted using
+ # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+ # compatibility. The encrypted string output can be decrypted using
# #public_decrypt.
#
# <b>Deprecated in version 3.0</b>.
@@ -378,7 +387,8 @@ module OpenSSL::PKey
# rsa.public_decrypt(string, padding) -> String
#
# Decrypt +string+, which has been encrypted with the private key, with the
- # public key. +padding+ defaults to PKCS1_PADDING.
+ # public key. +padding+ defaults to PKCS1_PADDING which is known to be
+ # insecure but is kept for backwards compatibility.
#
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
@@ -399,7 +409,8 @@ module OpenSSL::PKey
# rsa.public_encrypt(string, padding) -> String
#
# Encrypt +string+ with the public key. +padding+ defaults to
- # PKCS1_PADDING. The encrypted string output can be decrypted using
+ # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+ # compatibility. The encrypted string output can be decrypted using
# #private_decrypt.
#
# <b>Deprecated in version 3.0</b>.
@@ -420,7 +431,8 @@ module OpenSSL::PKey
# rsa.private_decrypt(string, padding) -> String
#
# Decrypt +string+, which has been encrypted with the public key, with the
- # private key. +padding+ defaults to PKCS1_PADDING.
+ # private key. +padding+ defaults to PKCS1_PADDING, which is known to be
+ # insecure but is kept for backwards compatibility.
#
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index a9103ecd27..ea8bb2a18e 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -11,6 +11,9 @@
=end
require "openssl/buffering"
+
+if defined?(OpenSSL::SSL)
+
require "io/nonblock"
require "ipaddr"
require "socket"
@@ -540,3 +543,5 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
end
end
+
+end
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
index 5e60604353..4163f55064 100644
--- a/ext/openssl/lib/openssl/version.rb
+++ b/ext/openssl/lib/openssl/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module OpenSSL
- VERSION = "3.0.0"
+ VERSION = "3.1.0"
end
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index c6cd818336..8d83b69193 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "openssl"
- spec.version = "3.0.0"
+ spec.version = "3.1.0"
spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
spec.email = ["ruby-core@ruby-lang.org"]
spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index 2ab8aeaebb..facb80aa73 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -52,6 +52,12 @@
(LIBRESSL_VERSION_NUMBER >= ((maj << 28) | (min << 20) | (pat << 12)))
#endif
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_3_const const
+#else
+# define OSSL_3_const /* const */
+#endif
+
#if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0)
# define OSSL_USE_ENGINE
#endif
diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c
index 0d3fa9ad15..71c452c88a 100644
--- a/ext/openssl/ossl_asn1.c
+++ b/ext/openssl/ossl_asn1.c
@@ -509,7 +509,8 @@ ossl_asn1_get_asn1type(VALUE obj)
ASN1_TYPE *ret;
VALUE value, rflag;
void *ptr;
- void (*free_func)();
+ typedef void free_func_type(void *);
+ free_func_type *free_func;
int tag;
tag = ossl_asn1_default_tag(obj);
@@ -522,16 +523,16 @@ ossl_asn1_get_asn1type(VALUE obj)
case V_ASN1_INTEGER: /* FALLTHROUGH */
case V_ASN1_ENUMERATED:
ptr = obj_to_asn1int(value);
- free_func = ASN1_INTEGER_free;
+ free_func = (free_func_type *)ASN1_INTEGER_free;
break;
case V_ASN1_BIT_STRING:
rflag = rb_attr_get(obj, sivUNUSED_BITS);
ptr = obj_to_asn1bstr(value, NUM2INT(rflag));
- free_func = ASN1_BIT_STRING_free;
+ free_func = (free_func_type *)ASN1_BIT_STRING_free;
break;
case V_ASN1_NULL:
ptr = obj_to_asn1null(value);
- free_func = ASN1_NULL_free;
+ free_func = (free_func_type *)ASN1_NULL_free;
break;
case V_ASN1_OCTET_STRING: /* FALLTHROUGH */
case V_ASN1_UTF8STRING: /* FALLTHROUGH */
@@ -546,24 +547,24 @@ ossl_asn1_get_asn1type(VALUE obj)
case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */
case V_ASN1_BMPSTRING:
ptr = obj_to_asn1str(value);
- free_func = ASN1_STRING_free;
+ free_func = (free_func_type *)ASN1_STRING_free;
break;
case V_ASN1_OBJECT:
ptr = obj_to_asn1obj(value);
- free_func = ASN1_OBJECT_free;
+ free_func = (free_func_type *)ASN1_OBJECT_free;
break;
case V_ASN1_UTCTIME:
ptr = obj_to_asn1utime(value);
- free_func = ASN1_TIME_free;
+ free_func = (free_func_type *)ASN1_TIME_free;
break;
case V_ASN1_GENERALIZEDTIME:
ptr = obj_to_asn1gtime(value);
- free_func = ASN1_TIME_free;
+ free_func = (free_func_type *)ASN1_TIME_free;
break;
case V_ASN1_SET: /* FALLTHROUGH */
case V_ASN1_SEQUENCE:
ptr = obj_to_asn1derstr(obj);
- free_func = ASN1_STRING_free;
+ free_func = (free_func_type *)ASN1_STRING_free;
break;
default:
ossl_raise(eASN1Error, "unsupported ASN.1 type");
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index 56fa0ec302..bf2bac3679 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -577,22 +577,33 @@ BIGNUM_2c(gcd)
*/
BIGNUM_2c(mod_sqr)
+#define BIGNUM_2cr(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE other) \
+ { \
+ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \
+ VALUE obj; \
+ GetBN(self, bn1); \
+ obj = NewBN(rb_obj_class(self)); \
+ if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \
+ ossl_raise(eBNError, NULL); \
+ SetBN(obj, result); \
+ return obj; \
+ }
+
/*
+ * Document-method: OpenSSL::BN#mod_sqrt
+ * call-seq:
+ * bn.mod_sqrt(bn2) => aBN
+ */
+BIGNUM_2cr(mod_sqrt)
+
+/*
+ * Document-method: OpenSSL::BN#mod_inverse
* call-seq:
* bn.mod_inverse(bn2) => aBN
*/
-static VALUE
-ossl_bn_mod_inverse(VALUE self, VALUE other)
-{
- BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;
- VALUE obj;
- GetBN(self, bn1);
- obj = NewBN(rb_obj_class(self));
- if (!(result = BN_mod_inverse(NULL, bn1, bn2, ossl_bn_ctx)))
- ossl_raise(eBNError, "BN_mod_inverse");
- SetBN(obj, result);
- return obj;
-}
+BIGNUM_2cr(mod_inverse)
/*
* call-seq:
@@ -1234,6 +1245,7 @@ Init_ossl_bn(void)
rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2);
rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2);
rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1);
+ rb_define_method(cBN, "mod_sqrt", ossl_bn_mod_sqrt, 1);
rb_define_method(cBN, "**", ossl_bn_exp, 1);
rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2);
rb_define_method(cBN, "gcd", ossl_bn_gcd, 1);
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index d9c7891433..cb8fbc3ca2 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -384,8 +384,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
StringValue(data);
in = (unsigned char *)RSTRING_PTR(data);
- if ((in_len = RSTRING_LEN(data)) == 0)
- ossl_raise(rb_eArgError, "data must not be empty");
+ in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
if (out_len <= 0) {
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index bfe3a74b12..1a5f471a27 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -97,11 +97,19 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
GetHMAC(self, ctx);
StringValue(key);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
+ (unsigned char *)RSTRING_PTR(key),
+ RSTRING_LENINT(key));
+ if (!pkey)
+ ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key");
+#else
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
(unsigned char *)RSTRING_PTR(key),
RSTRING_LENINT(key));
if (!pkey)
ossl_raise(eHMACError, "EVP_PKEY_new_mac_key");
+#endif
if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
NULL, pkey) != 1) {
EVP_PKEY_free(pkey);
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index 7fa38b865e..0d25a7304b 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -3,7 +3,7 @@
* Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors
*/
#include "ossl.h"
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
# include <openssl/kdf.h>
#endif
@@ -141,7 +141,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self)
}
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
/*
* call-seq:
* KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
@@ -305,7 +305,7 @@ Init_ossl_kdf(void)
#if defined(HAVE_EVP_PBE_SCRYPT)
rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1);
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1);
#endif
}
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 24d0da4683..476256679b 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -99,17 +99,56 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
/* First check DER */
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ OSSL_BIO_reset(bio);
/* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
- OSSL_BIO_reset(bio);
if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
goto out;
- while (OSSL_DECODER_from_bio(dctx, bio) != 1) {
- if (BIO_eof(bio))
+ /*
+ * First check for private key formats. This is to keep compatibility with
+ * ruby/openssl < 3.0 which decoded the following as a private key.
+ *
+ * $ openssl ecparam -name prime256v1 -genkey -outform PEM
+ * -----BEGIN EC PARAMETERS-----
+ * BggqhkjOPQMBBw==
+ * -----END EC PARAMETERS-----
+ * -----BEGIN EC PRIVATE KEY-----
+ * MHcCAQEEIAG8ugBbA5MHkqnZ9ujQF93OyUfL9tk8sxqM5Wv5tKg5oAoGCCqGSM49
+ * AwEHoUQDQgAEVcjhJfkwqh5C7kGuhAf8XaAjVuG5ADwb5ayg/cJijCgs+GcXeedj
+ * 86avKpGH84DXUlB23C/kPt+6fXYlitUmXQ==
+ * -----END EC PRIVATE KEY-----
+ *
+ * While the first PEM block is a proper encoding of ECParameters, thus
+ * OSSL_DECODER_from_bio() would pick it up, ruby/openssl used to return
+ * the latter instead. Existing applications expect this behavior.
+ *
+ * Note that normally, the input is supposed to contain a single decodable
+ * PEM block only, so this special handling should not create a new problem.
+ */
+ OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ if (BIO_eof(bio))
+ break;
pos2 = BIO_tell(bio);
if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
+ pos = pos2;
+ }
+
+ OSSL_BIO_reset(bio);
+ OSSL_DECODER_CTX_set_selection(dctx, 0);
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
+ if (BIO_eof(bio))
+ break;
+ pos2 = BIO_tell(bio);
+ if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
pos = pos2;
}
@@ -200,6 +239,7 @@ static VALUE
pkey_ctx_apply_options0(VALUE args_v)
{
VALUE *args = (VALUE *)args_v;
+ Check_Type(args[1], T_HASH);
rb_block_call(args[1], rb_intern("each"), 0, NULL,
pkey_ctx_apply_options_i, args[0]);
@@ -911,7 +951,7 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self)
rb_jump_tag(state);
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0)
if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data),
RSTRING_LEN(data)) < 1) {
EVP_MD_CTX_free(ctx);
@@ -1016,7 +1056,7 @@ ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
rb_jump_tag(state);
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0)
ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig),
RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data),
RSTRING_LEN(data));
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
index 38fb9fad10..10669b824c 100644
--- a/ext/openssl/ossl_pkey.h
+++ b/ext/openssl/ossl_pkey.h
@@ -92,7 +92,7 @@ void Init_ossl_ec(void);
*/ \
static VALUE ossl_##_keytype##_get_##_name(VALUE self) \
{ \
- _type *obj; \
+ const _type *obj; \
const BIGNUM *bn; \
\
Get##_type(self, obj); \
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
index 696455dcfd..83c41378fe 100644
--- a/ext/openssl/ossl_pkey_dh.c
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -178,7 +178,7 @@ ossl_dh_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_dh_is_public(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
const BIGNUM *bn;
GetDH(self, dh);
@@ -197,14 +197,14 @@ ossl_dh_is_public(VALUE self)
static VALUE
ossl_dh_is_private(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
const BIGNUM *bn;
GetDH(self, dh);
DH_get0_key(dh, NULL, &bn);
#if !defined(OPENSSL_NO_ENGINE)
- return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse;
+ return (bn || DH_get0_engine((DH *)dh)) ? Qtrue : Qfalse;
#else
return bn ? Qtrue : Qfalse;
#endif
@@ -223,7 +223,7 @@ ossl_dh_is_private(VALUE self)
static VALUE
ossl_dh_export(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
BIO *out;
VALUE str;
@@ -252,7 +252,7 @@ ossl_dh_export(VALUE self)
static VALUE
ossl_dh_to_der(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
unsigned char *p;
long len;
VALUE str;
@@ -280,7 +280,7 @@ ossl_dh_to_der(VALUE self)
static VALUE
ossl_dh_get_params(VALUE self)
{
- DH *dh;
+ OSSL_3_const DH *dh;
VALUE hash;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index 25404aa7f5..b097f8c9d2 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -24,7 +24,7 @@
} while (0)
static inline int
-DSA_HAS_PRIVATE(DSA *dsa)
+DSA_HAS_PRIVATE(OSSL_3_const DSA *dsa)
{
const BIGNUM *bn;
DSA_get0_key(dsa, NULL, &bn);
@@ -32,7 +32,7 @@ DSA_HAS_PRIVATE(DSA *dsa)
}
static inline int
-DSA_PRIVATE(VALUE obj, DSA *dsa)
+DSA_PRIVATE(VALUE obj, OSSL_3_const DSA *dsa)
{
return DSA_HAS_PRIVATE(dsa) || OSSL_PKEY_IS_PRIVATE(obj);
}
@@ -179,7 +179,7 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_dsa_is_public(VALUE self)
{
- DSA *dsa;
+ const DSA *dsa;
const BIGNUM *bn;
GetDSA(self, dsa);
@@ -198,7 +198,7 @@ ossl_dsa_is_public(VALUE self)
static VALUE
ossl_dsa_is_private(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
@@ -225,7 +225,7 @@ ossl_dsa_is_private(VALUE self)
static VALUE
ossl_dsa_export(int argc, VALUE *argv, VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
if (DSA_HAS_PRIVATE(dsa))
@@ -244,7 +244,7 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_dsa_to_der(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
GetDSA(self, dsa);
if (DSA_HAS_PRIVATE(dsa))
@@ -265,7 +265,7 @@ ossl_dsa_to_der(VALUE self)
static VALUE
ossl_dsa_get_params(VALUE self)
{
- DSA *dsa;
+ OSSL_3_const DSA *dsa;
VALUE hash;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index 08972e92a1..92842f95ac 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -227,7 +227,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_ec_key_get_group(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const EC_GROUP *group;
GetEC(self, ec);
@@ -272,7 +272,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v)
*/
static VALUE ossl_ec_key_get_private_key(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const BIGNUM *bn;
GetEC(self, ec);
@@ -323,7 +323,7 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
*/
static VALUE ossl_ec_key_get_public_key(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
const EC_POINT *point;
GetEC(self, ec);
@@ -375,7 +375,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
*/
static VALUE ossl_ec_key_is_public(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
@@ -391,7 +391,7 @@ static VALUE ossl_ec_key_is_public(VALUE self)
*/
static VALUE ossl_ec_key_is_private(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
@@ -411,9 +411,11 @@ static VALUE ossl_ec_key_is_private(VALUE self)
static VALUE
ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
+ if (EC_KEY_get0_public_key(ec) == NULL)
+ ossl_raise(eECError, "can't export - no public key set");
if (EC_KEY_get0_private_key(ec))
return ossl_pkey_export_traditional(argc, argv, self, 0);
else
@@ -429,9 +431,11 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_ec_key_to_der(VALUE self)
{
- EC_KEY *ec;
+ OSSL_3_const EC_KEY *ec;
GetEC(self, ec);
+ if (EC_KEY_get0_public_key(ec) == NULL)
+ ossl_raise(eECError, "can't export - no public key set");
if (EC_KEY_get0_private_key(ec))
return ossl_pkey_export_traditional(0, NULL, self, 1);
else
@@ -479,16 +483,28 @@ static VALUE ossl_ec_key_check_key(VALUE self)
#ifdef HAVE_EVP_PKEY_CHECK
EVP_PKEY *pkey;
EVP_PKEY_CTX *pctx;
- int ret;
+ const EC_KEY *ec;
GetPKey(self, pkey);
+ GetEC(self, ec);
pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
if (!pctx)
- ossl_raise(eDHError, "EVP_PKEY_CTX_new");
- ret = EVP_PKEY_public_check(pctx);
+ ossl_raise(eECError, "EVP_PKEY_CTX_new");
+
+ if (EC_KEY_get0_private_key(ec) != NULL) {
+ if (EVP_PKEY_check(pctx) != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eECError, "EVP_PKEY_check");
+ }
+ }
+ else {
+ if (EVP_PKEY_public_check(pctx) != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eECError, "EVP_PKEY_public_check");
+ }
+ }
+
EVP_PKEY_CTX_free(pctx);
- if (ret != 1)
- ossl_raise(eECError, "EVP_PKEY_public_check");
#else
EC_KEY *ec;
@@ -664,10 +680,11 @@ static VALUE ossl_ec_group_eql(VALUE a, VALUE b)
GetECGroup(a, group1);
GetECGroup(b, group2);
- if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1)
- return Qfalse;
-
- return Qtrue;
+ switch (EC_GROUP_cmp(group1, group2, ossl_bn_ctx)) {
+ case 0: return Qtrue;
+ case 1: return Qfalse;
+ default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp");
+ }
}
/*
@@ -1228,10 +1245,13 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b)
GetECPoint(b, point2);
GetECGroup(group_v1, group);
- if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1)
- return Qfalse;
+ switch (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx)) {
+ case 0: return Qtrue;
+ case 1: return Qfalse;
+ default: ossl_raise(eEC_POINT, "EC_POINT_cmp");
+ }
- return Qtrue;
+ UNREACHABLE;
}
/*
@@ -1249,7 +1269,7 @@ static VALUE ossl_ec_point_is_at_infinity(VALUE self)
switch (EC_POINT_is_at_infinity(group, point)) {
case 1: return Qtrue;
case 0: return Qfalse;
- default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity");
+ default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity");
}
UNREACHABLE;
@@ -1270,7 +1290,7 @@ static VALUE ossl_ec_point_is_on_curve(VALUE self)
switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) {
case 1: return Qtrue;
case 0: return Qfalse;
- default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve");
+ default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve");
}
UNREACHABLE;
@@ -1293,7 +1313,7 @@ static VALUE ossl_ec_point_make_affine(VALUE self)
rb_warn("OpenSSL::PKey::EC::Point#make_affine! is deprecated");
#if !OSSL_OPENSSL_PREREQ(3, 0, 0)
if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_make_affine");
+ ossl_raise(eEC_POINT, "EC_POINT_make_affine");
#endif
return self;
@@ -1312,7 +1332,7 @@ static VALUE ossl_ec_point_invert(VALUE self)
GetECPointGroup(self, group);
if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_invert");
+ ossl_raise(eEC_POINT, "EC_POINT_invert");
return self;
}
@@ -1330,7 +1350,7 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self)
GetECPointGroup(self, group);
if (EC_POINT_set_to_infinity(group, point) != 1)
- ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity");
+ ossl_raise(eEC_POINT, "EC_POINT_set_to_infinity");
return self;
}
@@ -1462,7 +1482,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self)
"use #mul(bn) form instead");
num = RARRAY_LEN(arg1);
- bns_tmp = rb_ary_hidden_new(num);
+ bns_tmp = rb_ary_tmp_new(num);
bignums = ALLOCV_N(const BIGNUM *, tmp_b, num);
for (i = 0; i < num; i++) {
VALUE item = RARRAY_AREF(arg1, i);
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 4d66010f49..072adabe62 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -24,7 +24,7 @@
} while (0)
static inline int
-RSA_HAS_PRIVATE(RSA *rsa)
+RSA_HAS_PRIVATE(OSSL_3_const RSA *rsa)
{
const BIGNUM *e, *d;
@@ -33,7 +33,7 @@ RSA_HAS_PRIVATE(RSA *rsa)
}
static inline int
-RSA_PRIVATE(VALUE obj, RSA *rsa)
+RSA_PRIVATE(VALUE obj, OSSL_3_const RSA *rsa)
{
return RSA_HAS_PRIVATE(rsa) || OSSL_PKEY_IS_PRIVATE(obj);
}
@@ -174,7 +174,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other)
static VALUE
ossl_rsa_is_public(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
GetRSA(self, rsa);
/*
@@ -193,7 +193,7 @@ ossl_rsa_is_public(VALUE self)
static VALUE
ossl_rsa_is_private(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
GetRSA(self, rsa);
@@ -203,7 +203,7 @@ ossl_rsa_is_private(VALUE self)
static int
can_export_rsaprivatekey(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
GetRSA(self, rsa);
@@ -453,7 +453,7 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_rsa_get_params(VALUE self)
{
- RSA *rsa;
+ OSSL_3_const RSA *rsa;
VALUE hash;
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index af262d9f56..f63992664a 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -11,11 +11,15 @@
*/
#include "ossl.h"
+#ifndef OPENSSL_NO_SOCK
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
+#if !defined(OPENSSL_NO_NEXTPROTONEG) && !OSSL_IS_LIBRESSL
+# define OSSL_USE_NEXTPROTONEG
+#endif
+
#if !defined(TLS1_3_VERSION) && \
- defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER >= 0x3020000fL
+ OSSL_LIBRESSL_PREREQ(3, 2, 0) && !OSSL_LIBRESSL_PREREQ(3, 4, 0)
# define TLS1_3_VERSION 0x0304
#endif
@@ -30,7 +34,6 @@
} while (0)
VALUE mSSL;
-static VALUE mSSLExtConfig;
static VALUE eSSLError;
VALUE cSSLContext;
VALUE cSSLSocket;
@@ -49,7 +52,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
- id_i_verify_hostname;
+ id_i_verify_hostname, id_i_keylog_cb;
static ID id_i_io, id_i_context, id_i_hostname;
static int ossl_ssl_ex_vcb_idx;
@@ -291,7 +294,7 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
if (!pkey)
return NULL;
- return EVP_PKEY_get0_DH(pkey);
+ return (DH *)EVP_PKEY_get0_DH(pkey);
}
#endif /* OPENSSL_NO_DH */
@@ -441,6 +444,54 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
return 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ */
+
+struct ossl_call_keylog_cb_args {
+ VALUE ssl_obj;
+ const char * line;
+};
+
+static VALUE
+ossl_call_keylog_cb(VALUE args_v)
+{
+ VALUE sslctx_obj, cb, line_v;
+ struct ossl_call_keylog_cb_args *args = (struct ossl_call_keylog_cb_args *) args_v;
+
+ sslctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
+
+ cb = rb_attr_get(sslctx_obj, id_i_keylog_cb);
+ if (NIL_P(cb)) return Qnil;
+
+ line_v = rb_str_new_cstr(args->line);
+
+ return rb_funcall(cb, id_call, 2, args->ssl_obj, line_v);
+}
+
+static void
+ossl_sslctx_keylog_cb(const SSL *ssl, const char *line)
+{
+ VALUE ssl_obj;
+ struct ossl_call_keylog_cb_args args;
+ int state = 0;
+
+ OSSL_Debug("SSL keylog callback entered");
+
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ args.ssl_obj = ssl_obj;
+ args.line = line;
+
+ rb_protect(ossl_call_keylog_cb, (VALUE)&args, &state);
+ if (state) {
+ rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
+ }
+}
+#endif
+
static VALUE
ossl_call_session_remove_cb(VALUE ary)
{
@@ -655,7 +706,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out,
return SSL_TLSEXT_ERR_OK;
}
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
static int
ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg)
@@ -852,7 +903,7 @@ ossl_sslctx_setup(VALUE self)
val = rb_attr_get(self, id_i_verify_depth);
if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
val = rb_attr_get(self, id_i_npn_protocols);
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
@@ -911,6 +962,18 @@ ossl_sslctx_setup(VALUE self)
OSSL_Debug("SSL TLSEXT servername callback added");
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ */
+ if (RTEST(rb_attr_get(self, id_i_keylog_cb))) {
+ SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb);
+ OSSL_Debug("SSL keylog callback added");
+ }
+#endif
+
return Qtrue;
}
@@ -1478,7 +1541,6 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
/*
* SSLSocket class
*/
-#ifndef OPENSSL_NO_SOCK
static inline int
ssl_started(SSL *ssl)
{
@@ -1641,11 +1703,16 @@ no_exception_p(VALUE opts)
return 0;
}
+// Provided by Ruby 3.2.0 and later in order to support the default IO#timeout.
+#ifndef RUBY_IO_TIMEOUT_DEFAULT
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+#endif
+
static void
io_wait_writable(rb_io_t *fptr)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_writable(errno, fptr->self, Qnil);
+ rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
#else
rb_io_wait_writable(fptr->fd);
#endif
@@ -1655,14 +1722,14 @@ static void
io_wait_readable(rb_io_t *fptr)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_readable(errno, fptr->self, Qnil);
+ rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
#else
rb_io_wait_readable(fptr->fd);
#endif
}
static VALUE
-ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
+ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
{
SSL *ssl;
rb_io_t *fptr;
@@ -2381,7 +2448,7 @@ ossl_ssl_get_client_ca_list(VALUE self)
return ossl_x509name_sk2ary(ca);
}
-# ifndef OPENSSL_NO_NEXTPROTONEG
+# ifdef OSSL_USE_NEXTPROTONEG
/*
* call-seq:
* ssl.npn_protocol => String | nil
@@ -2431,6 +2498,49 @@ ossl_ssl_alpn_protocol(VALUE self)
/*
* call-seq:
+ * session.export_keying_material(label, length) -> String
+ *
+ * Enables use of shared session key material in accordance with RFC 5705.
+ */
+static VALUE
+ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self)
+{
+ SSL *ssl;
+ VALUE str;
+ VALUE label;
+ VALUE length;
+ VALUE context;
+ unsigned char *p;
+ size_t len;
+ int use_ctx = 0;
+ unsigned char *ctx = NULL;
+ size_t ctx_len = 0;
+ int ret;
+
+ rb_scan_args(argc, argv, "21", &label, &length, &context);
+ StringValue(label);
+
+ GetSSL(self, ssl);
+
+ len = (size_t)NUM2LONG(length);
+ str = rb_str_new(0, len);
+ p = (unsigned char *)RSTRING_PTR(str);
+ if (!NIL_P(context)) {
+ use_ctx = 1;
+ StringValue(context);
+ ctx = (unsigned char *)RSTRING_PTR(context);
+ ctx_len = RSTRING_LEN(context);
+ }
+ ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label),
+ RSTRING_LENINT(label), ctx, ctx_len, use_ctx);
+ if (ret == 0 || ret == -1) {
+ ossl_raise(eSSLError, "SSL_export_keying_material");
+ }
+ return str;
+}
+
+/*
+ * call-seq:
* ssl.tmp_key => PKey or nil
*
* Returns the ephemeral key used in case of forward secrecy cipher.
@@ -2458,6 +2568,7 @@ Init_ossl_ssl(void)
rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
#endif
+#ifndef OPENSSL_NO_SOCK
id_call = rb_intern_const("call");
ID_callback_state = rb_intern_const("callback_state");
@@ -2480,16 +2591,6 @@ Init_ossl_ssl(void)
*/
mSSL = rb_define_module_under(mOSSL, "SSL");
- /* Document-module: OpenSSL::ExtConfig
- *
- * This module contains configuration information about the SSL extension,
- * for example if socket support is enabled, or the host name TLS extension
- * is enabled. Constants in this module will always be defined, but contain
- * +true+ or +false+ values depending on the configuration of your OpenSSL
- * installation.
- */
- mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig");
-
/* Document-class: OpenSSL::SSL::SSLError
*
* Generic error class raised by SSLSocket and SSLContext.
@@ -2652,8 +2753,6 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("session_remove_cb"), 1, 1, Qfalse);
- rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
-
/*
* A callback invoked whenever a new handshake is initiated on an
* established connection. May be used to disable renegotiation entirely.
@@ -2674,7 +2773,7 @@ Init_ossl_ssl(void)
* end
*/
rb_attr(cSSLContext, rb_intern_const("renegotiation_cb"), 1, 1, Qfalse);
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
/*
* An Enumerable of Strings. Each String represents a protocol to be
* advertised as the list of supported protocols for Next Protocol
@@ -2736,6 +2835,29 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse);
+ /*
+ * A callback invoked when TLS key material is generated or received, in
+ * order to allow applications to store this keying material for debugging
+ * purposes.
+ *
+ * The callback is invoked with an SSLSocket and a string containing the
+ * key material in the format used by NSS for its SSLKEYLOGFILE debugging
+ * output.
+ *
+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ *
+ * === Example
+ *
+ * context.keylog_cb = proc do |_sock, line|
+ * File.open('ssl_keylog_file', "a") do |f|
+ * f.write("#{line}\n")
+ * end
+ * end
+ */
+ rb_attr(cSSLContext, rb_intern_const("keylog_cb"), 1, 1, Qfalse);
+
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
rb_define_private_method(cSSLContext, "set_minmax_proto_version",
@@ -2821,11 +2943,6 @@ Init_ossl_ssl(void)
* Document-class: OpenSSL::SSL::SSLSocket
*/
cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
-#ifdef OPENSSL_NO_SOCK
- rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue);
- rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1);
-#else
- rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse);
rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc);
rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1);
rb_undef_method(cSSLSocket, "initialize_copy");
@@ -2856,10 +2973,10 @@ Init_ossl_ssl(void)
rb_define_method(cSSLSocket, "peer_finished_message", ossl_ssl_get_peer_finished, 0);
rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
-# ifndef OPENSSL_NO_NEXTPROTONEG
+ rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1);
+# ifdef OSSL_USE_NEXTPROTONEG
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
# endif
-#endif
rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE));
rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER));
@@ -3016,8 +3133,10 @@ Init_ossl_ssl(void)
DefIVarID(alpn_select_cb);
DefIVarID(servername_cb);
DefIVarID(verify_hostname);
+ DefIVarID(keylog_cb);
DefIVarID(io);
DefIVarID(context);
DefIVarID(hostname);
+#endif /* !defined(OPENSSL_NO_SOCK) */
}
diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c
index 92eb1365fe..139a474b04 100644
--- a/ext/openssl/ossl_ssl_session.c
+++ b/ext/openssl/ossl_ssl_session.c
@@ -4,6 +4,7 @@
#include "ossl.h"
+#ifndef OPENSSL_NO_SOCK
VALUE cSSLSession;
static VALUE eSSLSession;
@@ -299,6 +300,7 @@ static VALUE ossl_ssl_session_to_text(VALUE self)
return ossl_membio2str(out);
}
+#endif /* !defined(OPENSSL_NO_SOCK) */
void Init_ossl_ssl_session(void)
{
@@ -307,6 +309,7 @@ void Init_ossl_ssl_session(void)
mSSL = rb_define_module_under(mOSSL, "SSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
#endif
+#ifndef OPENSSL_NO_SOCK
cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject);
eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError);
@@ -324,4 +327,5 @@ void Init_ossl_ssl_session(void)
rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0);
rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0);
rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0);
+#endif /* !defined(OPENSSL_NO_SOCK) */
}
diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c
index 996f184170..9443541645 100644
--- a/ext/openssl/ossl_x509cert.c
+++ b/ext/openssl/ossl_x509cert.c
@@ -642,12 +642,12 @@ ossl_x509_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509(self, x509);
- while ((ext = X509_delete_ext(x509, 0)))
- X509_EXTENSION_free(ext);
+ for (i = X509_get_ext_count(x509); i > 0; i--)
+ X509_EXTENSION_free(X509_delete_ext(x509, 0));
for (i=0; i<RARRAY_LEN(ary); i++) {
ext = GetX509ExtPtr(RARRAY_AREF(ary, i));
if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext */
- ossl_raise(eX509CertError, NULL);
+ ossl_raise(eX509CertError, "X509_add_ext");
}
}
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
index 863f0286c0..6c1d915370 100644
--- a/ext/openssl/ossl_x509crl.c
+++ b/ext/openssl/ossl_x509crl.c
@@ -474,12 +474,12 @@ ossl_x509crl_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509CRL(self, crl);
- while ((ext = X509_CRL_delete_ext(crl, 0)))
- X509_EXTENSION_free(ext);
+ for (i = X509_CRL_get_ext_count(crl); i > 0; i--)
+ X509_EXTENSION_free(X509_CRL_delete_ext(crl, 0));
for (i=0; i<RARRAY_LEN(ary); i++) {
ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); /* NO NEED TO DUP */
if (!X509_CRL_add_ext(crl, ext, -1)) {
- ossl_raise(eX509CRLError, NULL);
+ ossl_raise(eX509CRLError, "X509_CRL_add_ext");
}
}
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
index 6eb91e9c2f..77a7d3f2ff 100644
--- a/ext/openssl/ossl_x509req.c
+++ b/ext/openssl/ossl_x509req.c
@@ -380,13 +380,13 @@ ossl_x509req_set_attributes(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Attr);
}
GetX509Req(self, req);
- while ((attr = X509_REQ_delete_attr(req, 0)))
- X509_ATTRIBUTE_free(attr);
+ for (i = X509_REQ_get_attr_count(req); i > 0; i--)
+ X509_ATTRIBUTE_free(X509_REQ_delete_attr(req, 0));
for (i=0;i<RARRAY_LEN(ary); i++) {
item = RARRAY_AREF(ary, i);
attr = GetX509AttrPtr(item);
if (!X509_REQ_add1_attr(req, attr)) {
- ossl_raise(eX509ReqError, NULL);
+ ossl_raise(eX509ReqError, "X509_REQ_add1_attr");
}
}
return ary;
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
index 5fe6853430..10b8aa4ad6 100644
--- a/ext/openssl/ossl_x509revoked.c
+++ b/ext/openssl/ossl_x509revoked.c
@@ -223,13 +223,13 @@ ossl_x509revoked_set_extensions(VALUE self, VALUE ary)
OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
}
GetX509Rev(self, rev);
- while ((ext = X509_REVOKED_delete_ext(rev, 0)))
- X509_EXTENSION_free(ext);
+ for (i = X509_REVOKED_get_ext_count(rev); i > 0; i--)
+ X509_EXTENSION_free(X509_REVOKED_delete_ext(rev, 0));
for (i=0; i<RARRAY_LEN(ary); i++) {
item = RARRAY_AREF(ary, i);
ext = GetX509ExtPtr(item);
if(!X509_REVOKED_add_ext(rev, ext, -1)) {
- ossl_raise(eX509RevError, NULL);
+ ossl_raise(eX509RevError, "X509_REVOKED_add_ext");
}
}
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index 9a297529ca..7bdfd0eb39 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -338,6 +338,8 @@ class Pathname
#
# Appends a pathname fragment to +self+ to produce a new Pathname object.
+ # Since +other+ is considered as a path relative to +self+, if +other+ is
+ # an absolute path, the new Pathname object is created from just +other+.
#
# p1 = Pathname.new("/usr") # Pathname:/usr
# p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
@@ -399,6 +401,8 @@ class Pathname
#
# Joins the given pathnames onto +self+ to create a new Pathname object.
+ # This is effectively the same as using Pathname#+ to append +self+ and
+ # all arguments sequentially.
#
# path0 = Pathname.new("/usr") # Pathname:/usr
# path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby
diff --git a/ext/pathname/pathname.gemspec b/ext/pathname/pathname.gemspec
index c9c0b84e69..92bc02b0db 100644
--- a/ext/pathname/pathname.gemspec
+++ b/ext/pathname/pathname.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "pathname"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb
index 6d03870436..41daf8c238 100644
--- a/ext/psych/extconf.rb
+++ b/ext/psych/extconf.rb
@@ -6,39 +6,9 @@ if $mswin or $mingw or $cygwin
$CPPFLAGS << " -DYAML_DECLARE_STATIC"
end
-yaml_source = with_config("libyaml-source-dir") || enable_config("bundled-libyaml", false)
-unless yaml_source # default to pre-installed libyaml
- pkg_config('yaml-0.1')
- dir_config('libyaml')
- unless find_header('yaml.h') && find_library('yaml', 'yaml_get_version')
- yaml_source = true # fallback to the bundled source if exists
- end
-end
-
-if yaml_source == true
- # search the latest libyaml source under $srcdir
- yaml_source = Dir.glob("#{$srcdir}/yaml{,-*}/").max_by {|n| File.basename(n).scan(/\d+/).map(&:to_i)}
- unless yaml_source
- download_failure = "failed to download libyaml source. Try manually installing libyaml?"
- begin
- require_relative '../../tool/extlibs.rb'
- rescue LoadError
- # When running in ruby/ruby, we use miniruby and don't have stdlib.
- # Avoid LoadError because it aborts the whole build. Usually when
- # stdlib extension fail to configure we skip it and continue.
- raise download_failure
- end
- extlibs = ExtLibs.new(cache_dir: File.expand_path("../../tmp/download_cache", $srcdir))
- unless extlibs.process_under($srcdir)
- raise download_failure
- end
- yaml_source, = Dir.glob("#{$srcdir}/yaml-*/")
- raise "libyaml not found" unless yaml_source
- end
-elsif yaml_source
- yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]}
-end
+yaml_source = with_config("libyaml-source-dir")
if yaml_source
+ yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]}
yaml_source = yaml_source.chomp("/")
yaml_configure = "#{File.expand_path(yaml_source)}/configure"
unless File.exist?(yaml_configure)
@@ -66,6 +36,11 @@ if yaml_source
libyaml = "libyaml.#$LIBEXT"
$cleanfiles << libyaml
$LOCAL_LIBS.prepend("$(LIBYAML) ")
+else # default to pre-installed libyaml
+ pkg_config('yaml-0.1')
+ dir_config('libyaml')
+ find_header('yaml.h') or abort "yaml.h not found"
+ find_library('yaml', 'yaml_get_version') or abort "libyaml not found"
end
create_makefile 'psych' do |mk|
diff --git a/ext/psych/extlibs b/ext/psych/extlibs
deleted file mode 100644
index 108aad42af..0000000000
--- a/ext/psych/extlibs
+++ /dev/null
@@ -1,11 +0,0 @@
-ver = 0.2.5
-pkg = yaml-$(ver)
-
-https://github.com/yaml/libyaml/releases/download/$(ver)/$(pkg).tar.gz \
- rmd160:cc175ed640046722fb7790de828002633407b6b9 \
- sha256:c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4 \
- sha512:dadd7d8e0d88b5ebab005e5d521d56d541580198aa497370966b98c904586e642a1cd4f3881094eb57624f218d50db77417bbfd0ffdce50340f011e35e8c4c02 \
- #
-
-$(pkg)/config/config.guess -> /tool/config.guess
-$(pkg)/config/config.sub -> /tool/config.sub
diff --git a/ext/psych/lib/psych/parser.rb b/ext/psych/lib/psych/parser.rb
index 39bc8289be..2181c730e5 100644
--- a/ext/psych/lib/psych/parser.rb
+++ b/ext/psych/lib/psych/parser.rb
@@ -48,5 +48,18 @@ module Psych
@handler = handler
@external_encoding = ANY
end
+
+ ###
+ # call-seq:
+ # parser.parse(yaml)
+ #
+ # Parse the YAML document contained in +yaml+. Events will be called on
+ # the handler set on the parser instance.
+ #
+ # See Psych::Parser and Psych::Parser#handler
+
+ def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "<unknown>"
+ _native_parse @handler, yaml, path
+ end
end
end
diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb
index b50667c315..3cb4bf3c7e 100644
--- a/ext/psych/lib/psych/scalar_scanner.rb
+++ b/ext/psych/lib/psych/scalar_scanner.rb
@@ -63,7 +63,7 @@ module Psych
elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/)
require 'date'
begin
- class_loader.date.strptime(string, '%Y-%m-%d')
+ class_loader.date.strptime(string, '%F', Date::GREGORIAN)
rescue ArgumentError
string
end
diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb
index 0fdead154c..a592a6916c 100644
--- a/ext/psych/lib/psych/versions.rb
+++ b/ext/psych/lib/psych/versions.rb
@@ -2,9 +2,9 @@
module Psych
# The version of Psych you are using
- VERSION = '5.0.0.dev'
+ VERSION = '5.0.1'
if RUBY_ENGINE == 'jruby'
- DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
+ DEFAULT_SNAKEYAML_VERSION = '1.33'.freeze
end
end
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index cce5daf3bb..8614251ca9 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -80,7 +80,9 @@ module Psych
when "!ruby/object:DateTime"
class_loader.date_time
require 'date' unless defined? DateTime
- @ss.parse_time(o.value).to_datetime
+ t = @ss.parse_time(o.value)
+ DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) +
+ (t.subsec/86400)
when '!ruby/encoding'
::Encoding.find o.value
when "!ruby/object:Complex"
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index 316a3a9496..31858798e4 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -192,12 +192,13 @@ module Psych
register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
end
+ def visit_Date o
+ register o, visit_Integer(o.gregorian)
+ end
+
def visit_DateTime o
- formatted = if o.offset.zero?
- o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze)
- else
- o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze)
- end
+ t = o.italy
+ formatted = format_time t, t.offset.zero?
tag = '!ruby/object:DateTime'
register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
end
@@ -235,7 +236,6 @@ module Psych
end
alias :visit_TrueClass :visit_Integer
alias :visit_FalseClass :visit_Integer
- alias :visit_Date :visit_Integer
def visit_Float o
if o.nan?
@@ -482,8 +482,8 @@ module Psych
@emitter.end_mapping
end
- def format_time time
- if time.utc?
+ def format_time time, utc = time.utc?
+ if utc
time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
else
time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c
index f91475b835..9c5179cc44 100644
--- a/ext/psych/psych_parser.c
+++ b/ext/psych/psych_parser.c
@@ -245,18 +245,8 @@ static VALUE protected_event_location(VALUE pointer)
return rb_funcall3(args[0], id_event_location, 4, args + 1);
}
-/*
- * call-seq:
- * parser.parse(yaml)
- *
- * Parse the YAML document contained in +yaml+. Events will be called on
- * the handler set on the parser instance.
- *
- * See Psych::Parser and Psych::Parser#handler
- */
-static VALUE parse(int argc, VALUE *argv, VALUE self)
+static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path)
{
- VALUE yaml, path;
yaml_parser_t * parser;
yaml_event_t event;
int done = 0;
@@ -264,14 +254,6 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
int parser_encoding = YAML_ANY_ENCODING;
int encoding = rb_utf8_encindex();
rb_encoding * internal_enc = rb_default_internal_encoding();
- VALUE handler = rb_iv_get(self, "@handler");
-
- if (rb_scan_args(argc, argv, "11", &yaml, &path) == 1) {
- if(rb_respond_to(yaml, id_path))
- path = rb_funcall(yaml, id_path, 0);
- else
- path = rb_str_new2("<unknown>");
- }
TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser);
@@ -562,7 +544,7 @@ void Init_psych_parser(void)
rb_require("psych/syntax_error");
- rb_define_method(cPsychParser, "parse", parse, -1);
+ rb_define_private_method(cPsychParser, "_native_parse", parse, 3);
rb_define_method(cPsychParser, "mark", mark, 0);
id_read = rb_intern("read");
diff --git a/ext/pty/depend b/ext/pty/depend
index c43d3dcf9a..f251caae3f 100644
--- a/ext/pty/depend
+++ b/ext/pty/depend
@@ -181,5 +181,6 @@ pty.o: $(top_srcdir)/internal/process.h
pty.o: $(top_srcdir)/internal/signal.h
pty.o: $(top_srcdir)/internal/static_assert.h
pty.o: $(top_srcdir)/internal/warnings.h
+pty.o: $(top_srcdir)/shape.h
pty.o: pty.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/pty/lib/expect.rb b/ext/pty/lib/expect.rb
index 5dbfa09ae9..22cbf54115 100644
--- a/ext/pty/lib/expect.rb
+++ b/ext/pty/lib/expect.rb
@@ -1,19 +1,19 @@
# frozen_string_literal: true
$expect_verbose = false
-# Expect library adds the IO instance method #expect, which does similar act to
-# tcl's expect extension.
-#
-# In order to use this method, you must require expect:
-#
-# require 'expect'
-#
-# Please see #expect for usage.
class IO
# call-seq:
# IO#expect(pattern,timeout=9999999) -> Array
# IO#expect(pattern,timeout=9999999) { |result| ... } -> nil
#
+ # The +expect+ library adds instance method IO#expect,
+ # which is similar to the
+ # {TCL expect extension}[https://www.tcl.tk/man/expect5.31/expect.1.html].
+ #
+ # To use this method, you must require +expect+:
+ #
+ # require 'expect'
+ #
# Reads from the IO until the given +pattern+ matches or the +timeout+ is over.
#
# It returns an array with the read buffer, followed by the matches.
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index 155d215942..acec33f9bf 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -170,7 +170,7 @@ establishShell(int argc, VALUE *argv, struct pty_info *info,
{
int master, slave, status = 0;
rb_pid_t pid;
- char *p, *getenv();
+ char *p;
VALUE v;
struct child_info carg;
char errbuf[32];
diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c
index f71ed2bba9..f752eb7749 100644
--- a/ext/racc/cparse/cparse.c
+++ b/ext/racc/cparse/cparse.c
@@ -7,8 +7,6 @@
This library is free software.
You can distribute/modify this program under the same terms of ruby.
- $originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $
-
*/
#include <ruby.h>
@@ -24,7 +22,7 @@
Important Constants
----------------------------------------------------------------------- */
-#define RACC_VERSION "1.4.15"
+#define RACC_VERSION "1.6.2"
#define DEFAULT_TOKEN -1
#define ERROR_TOKEN 1
diff --git a/ext/readline/readline-ext.gemspec b/ext/readline/readline-ext.gemspec
index 0c6f70ba91..1e16edbfe6 100644
--- a/ext/readline/readline-ext.gemspec
+++ b/ext/readline/readline-ext.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "readline-ext"
- spec.version = "0.1.4"
+ spec.version = "0.1.5"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/ext/ripper/depend b/ext/ripper/depend
index 80a6d62346..856283e177 100644
--- a/ext/ripper/depend
+++ b/ext/ripper/depend
@@ -17,12 +17,9 @@ ripper.o: ripper.c
all: check
static: check
-ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y {$(VPATH)}id.h
+ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y $(top_srcdir)/defs/id.def
$(ECHO) extracting $@ from $(top_srcdir)/parse.y
- $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ \
- --vpath=$(VPATH)$(PATH_SEPARATOR)$(top_srcdir) id.h $(top_srcdir)/parse.y > ripper.tmp.y
- $(Q) $(RUBY) $(top_srcdir)/tool/pure_parser.rb ripper.tmp.y $(BISON)
- $(Q) $(RM) ripper.tmp.y.bak
+ $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb $(top_srcdir)/parse.y > ripper.tmp.y
$(Q) $(RUBY) $(srcdir)/tools/preproc.rb ripper.tmp.y --output=$@
$(Q) $(RM) ripper.tmp.y
@@ -234,6 +231,7 @@ ripper.o: $(top_srcdir)/internal/bits.h
ripper.o: $(top_srcdir)/internal/compile.h
ripper.o: $(top_srcdir)/internal/compilers.h
ripper.o: $(top_srcdir)/internal/complex.h
+ripper.o: $(top_srcdir)/internal/encoding.h
ripper.o: $(top_srcdir)/internal/error.h
ripper.o: $(top_srcdir)/internal/fixnum.h
ripper.o: $(top_srcdir)/internal/gc.h
@@ -255,6 +253,7 @@ ripper.o: $(top_srcdir)/internal/warnings.h
ripper.o: $(top_srcdir)/node.h
ripper.o: $(top_srcdir)/regenc.h
ripper.o: $(top_srcdir)/ruby_assert.h
+ripper.o: $(top_srcdir)/shape.h
ripper.o: $(top_srcdir)/symbol.h
ripper.o: ../../probes.h
ripper.o: eventids2.c
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index ac38663f2d..05687497ac 100644
--- a/ext/ripper/eventids2.c
+++ b/ext/ripper/eventids2.c
@@ -1,22 +1,3 @@
-enum {
- tIGNORED_NL = tLAST_TOKEN + 1,
-# define tIGNORED_NL ((enum yytokentype)tIGNORED_NL)
- tCOMMENT,
-# define tCOMMENT ((enum yytokentype)tCOMMENT)
- tEMBDOC_BEG,
-# define tEMBDOC_BEG ((enum yytokentype)tEMBDOC_BEG)
- tEMBDOC,
-# define tEMBDOC ((enum yytokentype)tEMBDOC)
- tEMBDOC_END,
-# define tEMBDOC_END ((enum yytokentype)tEMBDOC_END)
- tHEREDOC_BEG,
-# define tHEREDOC_BEG ((enum yytokentype)tHEREDOC_BEG)
- tHEREDOC_END,
-# define tHEREDOC_END ((enum yytokentype)tHEREDOC_END)
- k__END__,
-# define k__END__ ((enum yytokentype)k__END__)
-};
-
typedef struct {
ID ripper_id_backref;
ID ripper_id_backtick;
diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb
index 19c59e2ccc..a0f1cbeaa8 100644
--- a/ext/ripper/lib/ripper/lexer.rb
+++ b/ext/ripper/lib/ripper/lexer.rb
@@ -228,7 +228,7 @@ class Ripper
def on_heredoc_end(tok)
@buf.push Elem.new([lineno(), column()], __callee__, tok, state())
- @buf = @stack.pop
+ @buf = @stack.pop unless @stack.empty?
end
def _push_token(tok)
@@ -242,7 +242,12 @@ class Ripper
end
def on_error2(mesg, elem)
- @errors.push Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg)
+ if elem
+ elem = Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg)
+ else
+ elem = Elem.new([lineno(), column()], __callee__, token(), state(), mesg)
+ end
+ @errors.push elem
end
PARSER_EVENTS.grep(/_error\z/) do |e|
arity = PARSER_EVENT_TABLE.fetch(e)
diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb
index b838a78db7..cd85a5da61 100644
--- a/ext/ripper/tools/preproc.rb
+++ b/ext/ripper/tools/preproc.rb
@@ -47,7 +47,7 @@ def prelude(f, out)
when /\A%%/
out << "%%\n"
return
- when /\A%token/
+ when /\A%token/, /\A} <node>/
out << line.sub(/<\w+>/, '<val>')
when /\A%type/
out << line.sub(/<\w+>/, '<val>')
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index 071e3323bb..7406177de2 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -1285,7 +1285,7 @@ bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
if (ss == -1) {
int e;
- if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
@@ -1557,7 +1557,7 @@ bsock_recvmsg_internal(VALUE sock,
if (ss == -1) {
int e;
- if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, Qnil)) {
+ if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index 93196c924d..54c369f6fc 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -601,7 +601,7 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE socket)
if (n >= 0) return SSIZET2NUM(n);
- if (rb_io_maybe_wait_writable(errno, socket, Qnil)) {
+ if (rb_io_maybe_wait_writable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) {
continue;
}
diff --git a/ext/socket/depend b/ext/socket/depend
index ffe2fce844..28c5540cd6 100644
--- a/ext/socket/depend
+++ b/ext/socket/depend
@@ -197,6 +197,7 @@ ancdata.o: $(top_srcdir)/internal/string.h
ancdata.o: $(top_srcdir)/internal/thread.h
ancdata.o: $(top_srcdir)/internal/vm.h
ancdata.o: $(top_srcdir)/internal/warnings.h
+ancdata.o: $(top_srcdir)/shape.h
ancdata.o: ancdata.c
ancdata.o: constdefs.h
ancdata.o: rubysocket.h
@@ -388,6 +389,7 @@ basicsocket.o: $(top_srcdir)/internal/string.h
basicsocket.o: $(top_srcdir)/internal/thread.h
basicsocket.o: $(top_srcdir)/internal/vm.h
basicsocket.o: $(top_srcdir)/internal/warnings.h
+basicsocket.o: $(top_srcdir)/shape.h
basicsocket.o: basicsocket.c
basicsocket.o: constdefs.h
basicsocket.o: rubysocket.h
@@ -579,6 +581,7 @@ constants.o: $(top_srcdir)/internal/string.h
constants.o: $(top_srcdir)/internal/thread.h
constants.o: $(top_srcdir)/internal/vm.h
constants.o: $(top_srcdir)/internal/warnings.h
+constants.o: $(top_srcdir)/shape.h
constants.o: constants.c
constants.o: constdefs.c
constants.o: constdefs.h
@@ -771,6 +774,7 @@ ifaddr.o: $(top_srcdir)/internal/string.h
ifaddr.o: $(top_srcdir)/internal/thread.h
ifaddr.o: $(top_srcdir)/internal/vm.h
ifaddr.o: $(top_srcdir)/internal/warnings.h
+ifaddr.o: $(top_srcdir)/shape.h
ifaddr.o: constdefs.h
ifaddr.o: ifaddr.c
ifaddr.o: rubysocket.h
@@ -962,6 +966,7 @@ init.o: $(top_srcdir)/internal/string.h
init.o: $(top_srcdir)/internal/thread.h
init.o: $(top_srcdir)/internal/vm.h
init.o: $(top_srcdir)/internal/warnings.h
+init.o: $(top_srcdir)/shape.h
init.o: constdefs.h
init.o: init.c
init.o: rubysocket.h
@@ -1153,6 +1158,7 @@ ipsocket.o: $(top_srcdir)/internal/string.h
ipsocket.o: $(top_srcdir)/internal/thread.h
ipsocket.o: $(top_srcdir)/internal/vm.h
ipsocket.o: $(top_srcdir)/internal/warnings.h
+ipsocket.o: $(top_srcdir)/shape.h
ipsocket.o: constdefs.h
ipsocket.o: ipsocket.c
ipsocket.o: rubysocket.h
@@ -1344,6 +1350,7 @@ option.o: $(top_srcdir)/internal/string.h
option.o: $(top_srcdir)/internal/thread.h
option.o: $(top_srcdir)/internal/vm.h
option.o: $(top_srcdir)/internal/warnings.h
+option.o: $(top_srcdir)/shape.h
option.o: constdefs.h
option.o: option.c
option.o: rubysocket.h
@@ -1535,6 +1542,7 @@ raddrinfo.o: $(top_srcdir)/internal/string.h
raddrinfo.o: $(top_srcdir)/internal/thread.h
raddrinfo.o: $(top_srcdir)/internal/vm.h
raddrinfo.o: $(top_srcdir)/internal/warnings.h
+raddrinfo.o: $(top_srcdir)/shape.h
raddrinfo.o: constdefs.h
raddrinfo.o: raddrinfo.c
raddrinfo.o: rubysocket.h
@@ -1726,6 +1734,7 @@ socket.o: $(top_srcdir)/internal/string.h
socket.o: $(top_srcdir)/internal/thread.h
socket.o: $(top_srcdir)/internal/vm.h
socket.o: $(top_srcdir)/internal/warnings.h
+socket.o: $(top_srcdir)/shape.h
socket.o: constdefs.h
socket.o: rubysocket.h
socket.o: socket.c
@@ -1917,6 +1926,7 @@ sockssocket.o: $(top_srcdir)/internal/string.h
sockssocket.o: $(top_srcdir)/internal/thread.h
sockssocket.o: $(top_srcdir)/internal/vm.h
sockssocket.o: $(top_srcdir)/internal/warnings.h
+sockssocket.o: $(top_srcdir)/shape.h
sockssocket.o: constdefs.h
sockssocket.o: rubysocket.h
sockssocket.o: sockport.h
@@ -2108,6 +2118,7 @@ tcpserver.o: $(top_srcdir)/internal/string.h
tcpserver.o: $(top_srcdir)/internal/thread.h
tcpserver.o: $(top_srcdir)/internal/vm.h
tcpserver.o: $(top_srcdir)/internal/warnings.h
+tcpserver.o: $(top_srcdir)/shape.h
tcpserver.o: constdefs.h
tcpserver.o: rubysocket.h
tcpserver.o: sockport.h
@@ -2299,6 +2310,7 @@ tcpsocket.o: $(top_srcdir)/internal/string.h
tcpsocket.o: $(top_srcdir)/internal/thread.h
tcpsocket.o: $(top_srcdir)/internal/vm.h
tcpsocket.o: $(top_srcdir)/internal/warnings.h
+tcpsocket.o: $(top_srcdir)/shape.h
tcpsocket.o: constdefs.h
tcpsocket.o: rubysocket.h
tcpsocket.o: sockport.h
@@ -2490,6 +2502,7 @@ udpsocket.o: $(top_srcdir)/internal/string.h
udpsocket.o: $(top_srcdir)/internal/thread.h
udpsocket.o: $(top_srcdir)/internal/vm.h
udpsocket.o: $(top_srcdir)/internal/warnings.h
+udpsocket.o: $(top_srcdir)/shape.h
udpsocket.o: constdefs.h
udpsocket.o: rubysocket.h
udpsocket.o: sockport.h
@@ -2681,6 +2694,7 @@ unixserver.o: $(top_srcdir)/internal/string.h
unixserver.o: $(top_srcdir)/internal/thread.h
unixserver.o: $(top_srcdir)/internal/vm.h
unixserver.o: $(top_srcdir)/internal/warnings.h
+unixserver.o: $(top_srcdir)/shape.h
unixserver.o: constdefs.h
unixserver.o: rubysocket.h
unixserver.o: sockport.h
@@ -2872,6 +2886,7 @@ unixsocket.o: $(top_srcdir)/internal/string.h
unixsocket.o: $(top_srcdir)/internal/thread.h
unixsocket.o: $(top_srcdir)/internal/vm.h
unixsocket.o: $(top_srcdir)/internal/warnings.h
+unixsocket.o: $(top_srcdir)/shape.h
unixsocket.o: constdefs.h
unixsocket.o: rubysocket.h
unixsocket.o: sockport.h
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index b70a862414..37ff216560 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -316,6 +316,7 @@ end
netpacket/packet.h
net/ethernet.h
sys/un.h
+ afunix.h
ifaddrs.h
sys/ioctl.h
sys/sockio.h
@@ -551,7 +552,7 @@ EOS
end
if !have_macro("IPPROTO_IPV6", headers) && have_const("IPPROTO_IPV6", headers)
- IO.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name|
+ File.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name|
have_const(name, headers) unless $defs.include?("-DHAVE_CONST_#{name.upcase}")
}
end
@@ -655,12 +656,20 @@ EOS
end
hdr = "netinet6/in6.h"
- if /darwin/ =~ RUBY_PLATFORM and !try_compile(<<"SRC", nil, :werror=>true)
+ /darwin/ =~ RUBY_PLATFORM and
+ checking_for("if apple's #{hdr} needs s6_addr patch") {!try_compile(<<"SRC", nil, :werror=>true)} and
#include <netinet/in.h>
int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);}
SRC
- print "fixing apple's netinet6/in6.h ..."; $stdout.flush
- in6 = File.read("/usr/include/#{hdr}")
+ checking_for("fixing apple's #{hdr}", "%s") do
+ file = xpopen(%w"clang -include netinet/in.h -E -xc -", in: IO::NULL) do |f|
+ re = %r[^# *\d+ *"(.*/netinet/in\.h)"]
+ Logging.message " grep(#{re})\n"
+ f.read[re, 1]
+ end
+ Logging.message "Substitute from #{file}\n"
+
+ in6 = File.read(file)
if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do
i, r = $2.to_i.divmod(4)
if r.zero?
@@ -670,12 +679,12 @@ SRC
end
end
FileUtils.mkdir_p(File.dirname(hdr))
- open(hdr, "w") {|f| f.write(in6)}
+ File.write(hdr, in6)
$distcleanfiles << hdr
$distcleandirs << File.dirname(hdr)
- puts "done"
+ "done"
else
- puts "not needed"
+ "not needed"
end
end
create_makefile("socket")
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 0cff3d6794..557d4374a5 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -189,7 +189,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
if (slen >= 0) break;
- if (!rb_io_maybe_wait_readable(errno, socket, Qnil))
+ if (!rb_io_maybe_wait_readable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT))
rb_sys_fail("recvfrom(2)");
}
@@ -209,7 +209,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
else
return rb_assoc_new(str, Qnil);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case RECV_UNIX:
return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen));
#endif
@@ -705,7 +705,7 @@ rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len)
retry = 1;
goto retry;
default:
- if (!rb_io_maybe_wait_readable(error, io, Qnil)) break;
+ if (!rb_io_maybe_wait_readable(error, io, RUBY_IO_TIMEOUT_DEFAULT)) break;
retry = 0;
goto retry;
}
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index d756a32a5a..eecdc7d4b8 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
require 'socket.so'
-require 'io/wait'
+
+unless IO.method_defined?(:wait_writable, false)
+ # It's only required on older Rubies < v3.2:
+ require 'io/wait'
+end
class Addrinfo
# creates an Addrinfo object from the arguments.
@@ -197,7 +201,7 @@ class Addrinfo
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
begin
sock.ipv6only! if self.ipv6?
- sock.setsockopt(:SOCKET, :REUSEADDR, 1)
+ sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX
sock.bind(self)
sock.listen(backlog)
rescue Exception
@@ -606,7 +610,6 @@ class Socket < BasicSocket
# _opts_ may have following options:
#
# [:connect_timeout] specify the timeout in seconds.
- # [:resolv_timeout] specify the name resolution timeout in seconds.
#
# If a block is given, the block is called with the socket.
# The value of the block is returned.
diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb
index 577958a358..5e6c0668f6 100644
--- a/ext/socket/mkconstants.rb
+++ b/ext/socket/mkconstants.rb
@@ -626,6 +626,7 @@ SO_SNDTIMEO nil Send timeout
SO_ACCEPTCONN nil Socket has had listen() called on it
SO_USELOOPBACK nil Bypass hardware when possible
SO_ACCEPTFILTER nil There is an accept filter
+SO_USER_COOKIE nil Setting an identifier for ipfw purpose mainly
SO_DONTTRUNC nil Retain unread data
SO_WANTMORE nil Give a hint when more data is ready
SO_WANTOOBFLAG nil OOB data is wanted in MSG_FLAG on receive
@@ -661,6 +662,10 @@ SO_SELECT_ERR_QUEUE nil Make select() detect socket error queue with err
SO_BUSY_POLL nil Set the threshold in microseconds for low latency polling (Linux 3.11)
SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13)
SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14)
+SO_SETFIB nil Set the associated routing table for the socket (FreeBSD)
+SO_RTABLE nil Set the routing table for this socket (OpenBSD)
+SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19)
+SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12)
SOPRI_INTERACTIVE nil Interactive socket priority
SOPRI_NORMAL nil Normal socket priority
@@ -670,9 +675,11 @@ IPX_TYPE
TCP_NODELAY nil Don't delay sending to coalesce packets
TCP_MAXSEG nil Set maximum segment size
+TCP_CONNECTION_INFO nil Retrieve information about this socket (macOS)
TCP_CORK nil Don't send partial frames (Linux 2.2, glibc 2.2)
TCP_DEFER_ACCEPT nil Don't notify a listening socket until data is ready (Linux 2.4, glibc 2.2)
TCP_INFO nil Retrieve information about this socket (Linux 2.4, glibc 2.2)
+TCP_KEEPALIVE nil Idle time before keepalive probes are sent (macOS)
TCP_KEEPCNT nil Maximum number of keepalive probes allowed before dropping a connection (Linux 2.4, glibc 2.2)
TCP_KEEPIDLE nil Idle time before keepalive probes are sent (Linux 2.4, glibc 2.2)
TCP_KEEPINTVL nil Time between keepalive probes (Linux 2.4, glibc 2.2)
diff --git a/ext/socket/option.c b/ext/socket/option.c
index 2dbe6379c4..0d818d0c70 100644
--- a/ext/socket/option.c
+++ b/ext/socket/option.c
@@ -670,10 +670,10 @@ rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int
{
#if defined(HAVE_IF_INDEXTONAME)
char ifbuf[IFNAMSIZ];
- if (if_indextoname(ifindex, ifbuf) == NULL)
- return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
- else
+ if (if_indextoname(ifindex, ifbuf))
return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
+ else
+ return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
#else
# ifndef IFNAMSIZ
# define IFNAMSIZ (sizeof(unsigned int)*3+1)
@@ -1229,7 +1229,7 @@ sockopt_inspect(VALUE self)
else
rb_str_catf(ret, " optname:%d", optname);
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
else if (family == AF_UNIX) {
rb_str_catf(ret, " level:%d", level);
@@ -1393,7 +1393,7 @@ sockopt_inspect(VALUE self)
}
break;
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
switch (level) {
case 0:
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index b914aa6520..45b4cad38f 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -287,8 +287,9 @@ numeric_getaddrinfo(const char *node, const char *service,
void
rb_freeaddrinfo(struct rb_addrinfo *ai)
{
- if (!ai->allocated_by_malloc)
- freeaddrinfo(ai->ai);
+ if (!ai->allocated_by_malloc) {
+ if (ai->ai) freeaddrinfo(ai->ai);
+ }
else {
struct addrinfo *ai1, *ai2;
ai1 = ai->ai;
@@ -618,8 +619,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
family = rb_str_dup(rb_id2str(id));
}
else {
- sprintf(pbuf, "unknown:%d", sockaddr->sa_family);
- family = rb_str_new2(pbuf);
+ family = rb_sprintf("unknown:%d", sockaddr->sa_family);
}
addr1 = Qnil;
@@ -645,7 +645,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
return ary;
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
static long
unixsocket_len(const struct sockaddr_un *su, socklen_t socklen)
{
@@ -1018,7 +1018,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
static void
init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
{
@@ -1147,7 +1147,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
break;
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */
{
VALUE path = rb_ary_entry(sockaddr_ary, 1);
@@ -1287,7 +1287,7 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
}
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
struct sockaddr_un *addr = &sockaddr->un;
@@ -1623,7 +1623,7 @@ addrinfo_mdump(VALUE self)
afamily = rb_id2str(id);
switch(afamily_int) {
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai));
@@ -1716,7 +1716,7 @@ addrinfo_mload(VALUE self, VALUE ary)
v = rb_ary_entry(ary, 1);
switch(afamily) {
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
{
struct sockaddr_un uaddr;
@@ -2192,7 +2192,7 @@ addrinfo_ipv6_multicast_p(VALUE self)
}
/*
- * Returns true for IPv6 link local address (ff80::/10).
+ * Returns true for IPv6 link local address (fe80::/10).
* It returns false otherwise.
*/
static VALUE
@@ -2204,7 +2204,7 @@ addrinfo_ipv6_linklocal_p(VALUE self)
}
/*
- * Returns true for IPv6 site local address (ffc0::/10).
+ * Returns true for IPv6 site local address (fec0::/10).
* It returns false otherwise.
*/
static VALUE
@@ -2344,7 +2344,7 @@ addrinfo_ipv6_to_ipv4(VALUE self)
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
* addrinfo.unix_path => path
@@ -2492,7 +2492,7 @@ addrinfo_s_udp(VALUE self, VALUE host, VALUE port)
INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0));
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
@@ -2630,7 +2630,7 @@ rsock_init_addrinfo(void)
rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1);
rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2);
rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1);
#endif
@@ -2671,7 +2671,7 @@ rsock_init_addrinfo(void)
rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0);
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0);
#endif
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 9ec893ee8c..5f803ba0da 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -33,6 +33,9 @@
#endif
#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <iphlpapi.h>
# if defined(_MSC_VER)
# undef HAVE_TYPE_STRUCT_SOCKADDR_DL
# endif
@@ -69,6 +72,11 @@
# include <sys/un.h>
#endif
+#ifdef HAVE_AFUNIX_H
+// Windows doesn't have sys/un.h, but it does have afunix.h just to be special:
+# include <afunix.h>
+#endif
+
#if defined(HAVE_FCNTL)
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@@ -268,7 +276,7 @@ extern VALUE rb_cIPSocket;
extern VALUE rb_cTCPSocket;
extern VALUE rb_cTCPServer;
extern VALUE rb_cUDPSocket;
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
extern VALUE rb_cUNIXSocket;
extern VALUE rb_cUNIXServer;
#endif
@@ -336,7 +344,7 @@ VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len);
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len);
VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len);
socklen_t rsock_unix_sockaddr_len(VALUE path);
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index b1965deb9e..eb74f7a936 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -28,6 +28,10 @@ rsock_syserr_fail_host_port(int err, const char *mesg, VALUE host, VALUE port)
message = rb_sprintf("%s for %+"PRIsVALUE" port % "PRIsVALUE"",
mesg, host, port);
+ if (err == ETIMEDOUT) {
+ rb_exc_raise(rb_exc_new3(rb_eIOTimeoutError, message));
+ }
+
rb_syserr_fail_str(err, message);
}
@@ -1379,7 +1383,7 @@ sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
@@ -1467,7 +1471,7 @@ sockaddr_len(struct sockaddr *addr)
return (socklen_t)sizeof(struct sockaddr_in6);
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
return (socklen_t)sizeof(struct sockaddr_un);
#endif
@@ -2016,7 +2020,7 @@ Init_socket(void)
rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1);
diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c
index 3500107972..5224e48a96 100644
--- a/ext/socket/udpsocket.c
+++ b/ext/socket/udpsocket.c
@@ -170,7 +170,7 @@ udp_send_internal(VALUE v)
if (n >= 0) return RB_SSIZE2NUM(n);
- if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
goto retry;
}
}
diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c
index 3a899cca1f..0ea5ac083c 100644
--- a/ext/socket/unixserver.c
+++ b/ext/socket/unixserver.c
@@ -10,7 +10,7 @@
#include "rubysocket.h"
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
* UNIXServer.new(path) => unixserver
@@ -101,7 +101,7 @@ unix_sysaccept(VALUE server)
void
rsock_init_unixserver(void)
{
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* Document-class: UNIXServer < UNIXSocket
*
diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c
index ecffbd4e92..26ab76fc9f 100644
--- a/ext/socket/unixsocket.c
+++ b/ext/socket/unixsocket.c
@@ -10,7 +10,7 @@
#include "rubysocket.h"
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
struct unixsock_arg {
struct sockaddr_un *sockaddr;
socklen_t sockaddrlen;
@@ -43,6 +43,10 @@ unixsock_path_value(VALUE path)
}
}
#endif
+#ifdef _WIN32
+ /* UNIXSocket requires UTF-8 per spec. */
+ path = rb_str_export_to_enc(path, rb_utf8_encoding());
+#endif
return rb_get_path(path);
}
@@ -571,7 +575,7 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
void
rsock_init_unixsocket(void)
{
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* Document-class: UNIXSocket < BasicSocket
*
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 9ba2cd92a1..0054766dac 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -12,7 +12,7 @@
**********************************************************************/
-#define STRINGIO_VERSION "3.0.3"
+#define STRINGIO_VERSION "3.0.4"
#include "ruby.h"
#include "ruby/io.h"
@@ -257,9 +257,20 @@ strio_s_allocate(VALUE klass)
}
/*
- * call-seq: StringIO.new(string=""[, mode])
+ * call-seq:
+ * StringIO.new(string = '', mode = 'r+') -> new_stringio
+ *
+ * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
+ *
+ * Returns a new \StringIO instance formed from +string+ and +mode+;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
+ *
+ * strio = StringIO.new # => #<StringIO>
+ * strio.close
+ *
+ * The instance should be closed when no longer needed.
*
- * Creates new StringIO instance from with _string_ and _mode_.
+ * Related: StringIO.open (accepts block; closes automatically).
*/
static VALUE
strio_initialize(int argc, VALUE *argv, VALUE self)
@@ -392,11 +403,26 @@ strio_finalize(VALUE self)
}
/*
- * call-seq: StringIO.open(string=""[, mode]) {|strio| ...}
+ * call-seq:
+ * StringIO.open(string = '', mode = 'r+') {|strio| ... }
+ *
+ * Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
+ *
+ * Creates a new \StringIO instance formed from +string+ and +mode+;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes].
+ *
+ * With no block, returns the new instance:
+ *
+ * strio = StringIO.open # => #<StringIO>
*
- * Equivalent to StringIO.new except that when it is called with a block, it
- * yields with the new instance and closes it, and returns the result which
- * returned from the block.
+ * With a block, calls the block with the new instance
+ * and returns the block's value;
+ * closes the instance on block exit.
+ *
+ * StringIO.open {|strio| p strio }
+ * # => #<StringIO>
+ *
+ * Related: StringIO.new.
*/
static VALUE
strio_s_open(int argc, VALUE *argv, VALUE klass)
@@ -482,9 +508,23 @@ strio_unimpl(int argc, VALUE *argv, VALUE self)
}
/*
- * call-seq: strio.string -> string
+ * call-seq:
+ * string -> string
+ *
+ * Returns underlying string:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.string = 'bar'
+ * p strio.string
+ * end
*
- * Returns underlying String object, the subject of IO.
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ *
+ * Related: StringIO#string= (assigns the underlying string).
*/
static VALUE
strio_get_string(VALUE self)
@@ -494,9 +534,23 @@ strio_get_string(VALUE self)
/*
* call-seq:
- * strio.string = string -> string
+ * string = other_string -> other_string
+ *
+ * Assigns the underlying string as +other_string+, and sets position to zero;
+ * returns +other_string+:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.string = 'bar'
+ * p strio.string
+ * end
*
- * Changes underlying String object, the subject of IO.
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ *
+ * Related: StringIO#string (returns the underlying string).
*/
static VALUE
strio_set_string(VALUE self, VALUE string)
@@ -514,10 +568,13 @@ strio_set_string(VALUE self, VALUE string)
/*
* call-seq:
- * strio.close -> nil
+ * close -> nil
+ *
+ * Closes +self+ for both reading and writing.
*
- * Closes a StringIO. The stream is unavailable for any further data
- * operations; an +IOError+ is raised if such an attempt is made.
+ * Raises IOError if reading or writing is attempted.
+ *
+ * Related: StringIO#close_read, StringIO#close_write.
*/
static VALUE
strio_close(VALUE self)
@@ -529,10 +586,13 @@ strio_close(VALUE self)
/*
* call-seq:
- * strio.close_read -> nil
+ * close_read -> nil
+ *
+ * Closes +self+ for reading; closed-write setting remains unchanged.
*
- * Closes the read end of a StringIO. Will raise an +IOError+ if the
- * receiver is not readable.
+ * Raises IOError if reading is attempted.
+ *
+ * Related: StringIO#close, StringIO#close_write.
*/
static VALUE
strio_close_read(VALUE self)
@@ -547,10 +607,13 @@ strio_close_read(VALUE self)
/*
* call-seq:
- * strio.close_write -> nil
+ * close_write -> nil
+ *
+ * Closes +self+ for writing; closed-read setting remains unchanged.
+ *
+ * Raises IOError if writing is attempted.
*
- * Closes the write end of a StringIO. Will raise an +IOError+ if the
- * receiver is not writeable.
+ * Related: StringIO#close, StringIO#close_read.
*/
static VALUE
strio_close_write(VALUE self)
@@ -565,9 +628,10 @@ strio_close_write(VALUE self)
/*
* call-seq:
- * strio.closed? -> true or false
+ * closed? -> true or false
*
- * Returns +true+ if the stream is completely closed, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for both reading and writing,
+ * +false+ otherwise.
*/
static VALUE
strio_closed(VALUE self)
@@ -579,9 +643,9 @@ strio_closed(VALUE self)
/*
* call-seq:
- * strio.closed_read? -> true or false
+ * closed_read? -> true or false
*
- * Returns +true+ if the stream is not readable, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for reading, +false+ otherwise.
*/
static VALUE
strio_closed_read(VALUE self)
@@ -593,9 +657,9 @@ strio_closed_read(VALUE self)
/*
* call-seq:
- * strio.closed_write? -> true or false
+ * closed_write? -> true or false
*
- * Returns +true+ if the stream is not writable, +false+ otherwise.
+ * Returns +true+ if +self+ is closed for writing, +false+ otherwise.
*/
static VALUE
strio_closed_write(VALUE self)
@@ -615,11 +679,14 @@ strio_to_read(VALUE self)
/*
* call-seq:
- * strio.eof -> true or false
- * strio.eof? -> true or false
+ * eof? -> true or false
+ *
+ * Returns +true+ if positioned at end-of-stream, +false+ otherwise;
+ * see {Position}[rdoc-ref:File@Position].
+ *
+ * Raises IOError if the stream is not opened for reading.
*
- * Returns true if the stream is at the end of the data (underlying string).
- * The stream must be opened for reading or an +IOError+ will be raised.
+ * StreamIO#eof is an alias for StreamIO#eof?.
*/
static VALUE
strio_eof(VALUE self)
@@ -649,13 +716,10 @@ strio_copy(VALUE copy, VALUE orig)
/*
* call-seq:
- * strio.lineno -> integer
+ * lineno -> current_line_number
*
- * Returns the current line number. The stream must be
- * opened for reading. +lineno+ counts the number of times +gets+ is
- * called, rather than the number of newlines encountered. The two
- * values will differ if +gets+ is called with a separator other than
- * newline. See also the <code>$.</code> variable.
+ * Returns the current line number in +self+;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_get_lineno(VALUE self)
@@ -665,10 +729,10 @@ strio_get_lineno(VALUE self)
/*
* call-seq:
- * strio.lineno = integer -> integer
+ * lineno = new_line_number -> new_line_number
*
- * Manually sets the current line number to the given value.
- * <code>$.</code> is updated only on the next read.
+ * Sets the current line number in +self+ to the given +new_line_number+;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_set_lineno(VALUE self, VALUE lineno)
@@ -679,9 +743,10 @@ strio_set_lineno(VALUE self, VALUE lineno)
/*
* call-seq:
- * strio.binmode -> stringio
+ * binmode -> self
*
- * Puts stream into binary mode. See IO#binmode.
+ * Sets the data mode in +self+ to binary mode;
+ * see {Data Mode}[rdoc-ref:File@Data+Mode].
*
*/
static VALUE
@@ -705,11 +770,27 @@ strio_binmode(VALUE self)
/*
* call-seq:
- * strio.reopen(other_StrIO) -> strio
- * strio.reopen(string, mode) -> strio
+ * reopen(other, mode = 'r+') -> self
+ *
+ * Reinitializes the stream with the given +other+ (string or StringIO) and +mode+;
+ * see IO.new:
+ *
+ * StringIO.open('foo') do |strio|
+ * p strio.string
+ * strio.reopen('bar')
+ * p strio.string
+ * other_strio = StringIO.new('baz')
+ * strio.reopen(other_strio)
+ * p strio.string
+ * other_strio.close
+ * end
+ *
+ * Output:
+ *
+ * "foo"
+ * "bar"
+ * "baz"
*
- * Reinitializes the stream with the given <i>other_StrIO</i> or _string_
- * and _mode_ (see StringIO#new).
*/
static VALUE
strio_reopen(int argc, VALUE *argv, VALUE self)
@@ -723,10 +804,12 @@ strio_reopen(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.pos -> integer
- * strio.tell -> integer
+ * pos -> stream_position
+ *
+ * Returns the current position (in bytes);
+ * see {Position}[rdoc-ref:IO@Position].
*
- * Returns the current offset (in bytes).
+ * StringIO#tell is an alias for StringIO#pos.
*/
static VALUE
strio_get_pos(VALUE self)
@@ -736,9 +819,10 @@ strio_get_pos(VALUE self)
/*
* call-seq:
- * strio.pos = integer -> integer
+ * pos = new_position -> new_position
*
- * Seeks to the given position (in bytes).
+ * Sets the current position (in bytes);
+ * see {Position}[rdoc-ref:IO@Position].
*/
static VALUE
strio_set_pos(VALUE self, VALUE pos)
@@ -754,10 +838,11 @@ strio_set_pos(VALUE self, VALUE pos)
/*
* call-seq:
- * strio.rewind -> 0
+ * rewind -> 0
*
- * Positions the stream to the beginning of input, resetting
- * +lineno+ to zero.
+ * Sets the current position and line number to zero;
+ * see {Position}[rdoc-ref:IO@Position]
+ * and {Line Number}[rdoc-ref:IO@Line+Number].
*/
static VALUE
strio_rewind(VALUE self)
@@ -770,10 +855,11 @@ strio_rewind(VALUE self)
/*
* call-seq:
- * strio.seek(amount, whence=SEEK_SET) -> 0
+ * seek(offset, whence = SEEK_SET) -> 0
*
- * Seeks to a given offset _amount_ in the stream according to
- * the value of _whence_ (see IO#seek).
+ * Sets the current position to the given integer +offset+ (in bytes),
+ * with respect to a given constant +whence+;
+ * see {Position}[rdoc-ref:IO@Position].
*/
static VALUE
strio_seek(int argc, VALUE *argv, VALUE self)
@@ -809,9 +895,9 @@ strio_seek(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.sync -> true
+ * sync -> true
*
- * Returns +true+ always.
+ * Returns +true+; implemented only for compatibility with other stream classes.
*/
static VALUE
strio_get_sync(VALUE self)
@@ -826,10 +912,12 @@ strio_get_sync(VALUE self)
/*
* call-seq:
- * strio.each_byte {|byte| block } -> strio
- * strio.each_byte -> anEnumerator
+ * each_byte {|byte| ... } -> self
*
- * See IO#each_byte.
+ * With a block given, calls the block with each remaining byte in the stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
+ *
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_byte(VALUE self)
@@ -847,9 +935,10 @@ strio_each_byte(VALUE self)
/*
* call-seq:
- * strio.getc -> string or nil
+ * getc -> character or nil
*
- * See IO#getc.
+ * Reads and returns the next character from the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_getc(VALUE self)
@@ -872,9 +961,10 @@ strio_getc(VALUE self)
/*
* call-seq:
- * strio.getbyte -> fixnum or nil
+ * getbyte -> byte or nil
*
- * See IO#getbyte.
+ * Reads and returns the next 8-bit byte from the stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
*/
static VALUE
strio_getbyte(VALUE self)
@@ -910,12 +1000,10 @@ strio_extend(struct StringIO *ptr, long pos, long len)
/*
* call-seq:
- * strio.ungetc(string) -> nil
+ * ungetc(character) -> nil
*
- * Pushes back one character (passed as a parameter)
- * such that a subsequent buffered read will return it. There is no
- * limitation for multiple pushbacks including pushing back behind the
- * beginning of the buffer string.
+ * Pushes back ("unshifts") a character or integer onto the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_ungetc(VALUE self, VALUE c)
@@ -950,9 +1038,10 @@ strio_ungetc(VALUE self, VALUE c)
/*
* call-seq:
- * strio.ungetbyte(fixnum) -> nil
+ * ungetbyte(byte) -> nil
*
- * See IO#ungetbyte
+ * Pushes back ("unshifts") an 8-bit byte onto the stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
*/
static VALUE
strio_ungetbyte(VALUE self, VALUE c)
@@ -1012,9 +1101,10 @@ strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
/*
* call-seq:
- * strio.readchar -> string
+ * readchar -> string
*
- * See IO#readchar.
+ * Like +getc+, but raises an exception if already at end-of-stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*/
static VALUE
strio_readchar(VALUE self)
@@ -1026,9 +1116,10 @@ strio_readchar(VALUE self)
/*
* call-seq:
- * strio.readbyte -> fixnum
+ * readbyte -> byte
*
- * See IO#readbyte.
+ * Like +getbyte+, but raises an exception if already at end-of-stream;
+ * see {Byte IO}[rdoc-ref:IO@Byte+IO].
*/
static VALUE
strio_readbyte(VALUE self)
@@ -1040,10 +1131,12 @@ strio_readbyte(VALUE self)
/*
* call-seq:
- * strio.each_char {|char| block } -> strio
- * strio.each_char -> anEnumerator
+ * each_char {|c| ... } -> self
+ *
+ * With a block given, calls the block with each remaining character in the stream;
+ * see {Character IO}[rdoc-ref:IO@Character+IO].
*
- * See IO#each_char.
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_char(VALUE self)
@@ -1060,10 +1153,12 @@ strio_each_char(VALUE self)
/*
* call-seq:
- * strio.each_codepoint {|c| block } -> strio
- * strio.each_codepoint -> anEnumerator
+ * each_codepoint {|codepoint| ... } -> self
*
- * See IO#each_codepoint.
+ * With a block given, calls the block with each remaining codepoint in the stream;
+ * see {Codepoint IO}[rdoc-ref:IO@Codepoint+IO].
+ *
+ * With no block given, returns an enumerator.
*/
static VALUE
strio_each_codepoint(VALUE self)
@@ -1245,8 +1340,9 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else {
- if (n < e - s) {
- if (e - s < 1024) {
+ if (n < e - s + arg->chomp) {
+ /* unless chomping, RS at the end does not matter */
+ if (e - s < 1024 || n == e - s) {
for (p = s; p + n <= e; ++p) {
if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) {
e = p + n;
@@ -1273,11 +1369,13 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
/*
* call-seq:
- * strio.gets(sep=$/, chomp: false) -> string or nil
- * strio.gets(limit, chomp: false) -> string or nil
- * strio.gets(sep, limit, chomp: false) -> string or nil
+ * gets(sep = $/, chomp: false) -> string or nil
+ * gets(limit, chomp: false) -> string or nil
+ * gets(sep, limit, chomp: false) -> string or nil
*
- * See IO#gets.
+ * Reads and returns a line from the stream;
+ * assigns the return value to <tt>$_</tt>;
+ * see {Line IO}[rdoc-ref:IO@Line+IO].
*/
static VALUE
strio_gets(int argc, VALUE *argv, VALUE self)
@@ -1297,11 +1395,12 @@ strio_gets(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.readline(sep=$/, chomp: false) -> string
- * strio.readline(limit, chomp: false) -> string or nil
- * strio.readline(sep, limit, chomp: false) -> string or nil
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
- * See IO#readline.
+ * Reads a line as with IO#gets, but raises EOFError if already at end-of-file;
+ * see {Line IO}[rdoc-ref:IO@Line+IO].
*/
static VALUE
strio_readline(int argc, VALUE *argv, VALUE self)
@@ -1313,17 +1412,16 @@ strio_readline(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.each(sep=$/, chomp: false) {|line| block } -> strio
- * strio.each(limit, chomp: false) {|line| block } -> strio
- * strio.each(sep, limit, chomp: false) {|line| block } -> strio
- * strio.each(...) -> anEnumerator
+ * each_line(sep = $/, chomp: false) {|line| ... } -> self
+ * each_line(limit, chomp: false) {|line| ... } -> self
+ * each_line(sep, limit, chomp: false) {|line| ... } -> self
*
- * strio.each_line(sep=$/, chomp: false) {|line| block } -> strio
- * strio.each_line(limit, chomp: false) {|line| block } -> strio
- * strio.each_line(sep, limit, chomp: false) {|line| block } -> strio
- * strio.each_line(...) -> anEnumerator
+ * Calls the block with each remaining line read from the stream;
+ * does nothing if already at end-of-file;
+ * returns +self+.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
- * See IO#each.
+ * StringIO#each is an alias for StringIO#each_line.
*/
static VALUE
strio_each(int argc, VALUE *argv, VALUE self)
@@ -1751,24 +1849,16 @@ strio_set_encoding_by_bom(VALUE self)
}
/*
- * Pseudo I/O on String object, with interface corresponding to IO.
+ * \IO streams for strings, with access similar to
+ * {IO}[rdoc-ref:IO];
+ * see {IO}[rdoc-ref:IO].
*
- * Commonly used to simulate <code>$stdio</code> or <code>$stderr</code>
+ * === About the Examples
*
- * === Examples
+ * Examples on this page assume that \StringIO has been required:
*
* require 'stringio'
*
- * # Writing stream emulation
- * io = StringIO.new
- * io.puts "Hello World"
- * io.string #=> "Hello World\n"
- *
- * # Reading stream emulation
- * io = StringIO.new "first\nsecond\nlast\n"
- * io.getc #=> "f"
- * io.gets #=> "irst\n"
- * io.read #=> "second\nlast\n"
*/
void
Init_stringio(void)
diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb
index f0ecbf85d8..3c311d2364 100644
--- a/ext/strscan/extconf.rb
+++ b/ext/strscan/extconf.rb
@@ -1,5 +1,10 @@
# frozen_string_literal: true
require 'mkmf'
-$INCFLAGS << " -I$(top_srcdir)" if $extmk
-have_func("onig_region_memsize", "ruby.h")
-create_makefile 'strscan'
+if RUBY_ENGINE == 'ruby'
+ $INCFLAGS << " -I$(top_srcdir)" if $extmk
+ have_func("onig_region_memsize(NULL)")
+ have_func("rb_reg_onig_match", "ruby/re.h")
+ create_makefile 'strscan'
+else
+ File.write('Makefile', dummy_makefile("").join)
+end
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index e1426380b4..16d669d8a5 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs);
#include <stdbool.h>
-#define STRSCAN_VERSION "3.0.1"
+#define STRSCAN_VERSION "3.0.7"
/* =======================================================================
Data Type Definitions
@@ -435,11 +435,11 @@ strscan_get_pos(VALUE self)
*
* In short, it's a 0-based index into the string.
*
- * s = StringScanner.new("abcädeföghi")
- * s.charpos # -> 0
- * s.scan_until(/ä/) # -> "abcä"
- * s.pos # -> 5
- * s.charpos # -> 4
+ * s = StringScanner.new("abc\u00e4def\u00f6ghi")
+ * s.charpos # -> 0
+ * s.scan_until(/\u00e4/) # -> "abc\u00E4"
+ * s.pos # -> 5
+ * s.charpos # -> 4
*/
static VALUE
strscan_get_charpos(VALUE self)
@@ -539,6 +539,68 @@ adjust_register_position(struct strscanner *p, long position)
}
}
+/* rb_reg_onig_match is available in Ruby 3.3 and later. */
+#ifndef HAVE_RB_REG_ONIG_MATCH
+static OnigPosition
+rb_reg_onig_match(VALUE re, VALUE str,
+ OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args),
+ void *args, struct re_registers *regs)
+{
+ regex_t *reg = rb_reg_prepare_re(re, str);
+
+ bool tmpreg = reg != RREGEXP_PTR(re);
+ if (!tmpreg) RREGEXP(re)->usecnt++;
+
+ OnigPosition result = match(reg, str, regs, args);
+
+ if (!tmpreg) RREGEXP(re)->usecnt--;
+ if (tmpreg) {
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
+ }
+
+ if (result < 0) {
+ if (result != ONIG_MISMATCH) {
+ rb_raise(ScanError, "regexp buffer overflow");
+ }
+ }
+
+ return result;
+}
+#endif
+
+static OnigPosition
+strscan_match(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_match(reg,
+ match_target(p),
+ (UChar* )(CURPTR(p) + S_RESTLEN(p)),
+ (UChar* )CURPTR(p),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
+static OnigPosition
+strscan_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_search(reg,
+ match_target(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ (UChar *)CURPTR(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
static VALUE
strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly)
{
@@ -560,47 +622,14 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly
}
if (RB_TYPE_P(pattern, T_REGEXP)) {
- regex_t *rb_reg_prepare_re(VALUE re, VALUE str);
- regex_t *re;
- long ret;
- int tmpreg;
-
p->regex = pattern;
- re = rb_reg_prepare_re(pattern, p->str);
- tmpreg = re != RREGEXP_PTR(pattern);
- if (!tmpreg) RREGEXP(pattern)->usecnt++;
-
- if (headonly) {
- ret = onig_match(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- else {
- ret = onig_search(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- if (!tmpreg) RREGEXP(pattern)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(pattern)->usecnt) {
- onig_free(re);
- }
- else {
- onig_free(RREGEXP_PTR(pattern));
- RREGEXP_PTR(pattern) = re;
- }
- }
+ OnigPosition ret = rb_reg_onig_match(pattern,
+ p->str,
+ headonly ? strscan_match : strscan_search,
+ (void *)p,
+ &(p->regs));
- if (ret == -2) rb_raise(ScanError, "regexp buffer overflow");
- if (ret < 0) {
- /* not matched */
+ if (ret == ONIG_MISMATCH) {
return Qnil;
}
}
@@ -1038,8 +1067,9 @@ strscan_empty_p(VALUE self)
* This method is obsolete; use #eos? instead.
*
* s = StringScanner.new('test string')
- * s.eos? # These two
- * s.rest? # are opposites.
+ * # These two are opposites
+ * s.eos? # => false
+ * s.rest? # => true
*/
static VALUE
strscan_rest_p(VALUE self)
@@ -1458,6 +1488,56 @@ strscan_fixed_anchor_p(VALUE self)
return p->fixed_anchor_p ? Qtrue : Qfalse;
}
+typedef struct {
+ VALUE self;
+ VALUE captures;
+} named_captures_data;
+
+static int
+named_captures_iter(const OnigUChar *name,
+ const OnigUChar *name_end,
+ int back_num,
+ int *back_refs,
+ OnigRegex regex,
+ void *arg)
+{
+ named_captures_data *data = arg;
+
+ VALUE key = rb_str_new((const char *)name, name_end - name);
+ VALUE value = RUBY_Qnil;
+ int i;
+ for (i = 0; i < back_num; i++) {
+ value = strscan_aref(data->self, INT2NUM(back_refs[i]));
+ }
+ rb_hash_aset(data->captures, key, value);
+ return 0;
+}
+
+/*
+ * call-seq:
+ * scanner.named_captures -> hash
+ *
+ * Returns a hash of string variables matching the regular expression.
+ *
+ * scan = StringScanner.new('foobarbaz')
+ * scan.match?(/(?<f>foo)(?<r>bar)(?<z>baz)/)
+ * scan.named_captures # -> {"f"=>"foo", "r"=>"bar", "z"=>"baz"}
+ */
+static VALUE
+strscan_named_captures(VALUE self)
+{
+ struct strscanner *p;
+ GET_SCANNER(self, p);
+ named_captures_data data;
+ data.self = self;
+ data.captures = rb_hash_new();
+ if (!RB_NIL_P(p->regex)) {
+ onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data);
+ }
+
+ return data.captures;
+}
+
/* =======================================================================
Ruby Interface
======================================================================= */
@@ -1468,6 +1548,8 @@ strscan_fixed_anchor_p(VALUE self)
* StringScanner provides for lexical scanning operations on a String. Here is
* an example of its usage:
*
+ * require 'strscan'
+ *
* s = StringScanner.new('This is an example string')
* s.eos? # -> false
*
@@ -1650,4 +1732,6 @@ Init_strscan(void)
rb_define_method(StringScanner, "inspect", strscan_inspect, 0);
rb_define_method(StringScanner, "fixed_anchor?", strscan_fixed_anchor_p, 0);
+
+ rb_define_method(StringScanner, "named_captures", strscan_named_captures, 0);
}
diff --git a/ext/strscan/strscan.gemspec b/ext/strscan/strscan.gemspec
index 5d8119ea4c..8a61c7abe6 100644
--- a/ext/strscan/strscan.gemspec
+++ b/ext/strscan/strscan.gemspec
@@ -16,13 +16,26 @@ Gem::Specification.new do |s|
s.summary = "Provides lexical scanning operations on a String."
s.description = "Provides lexical scanning operations on a String."
- s.require_path = %w{lib}
- s.files = %w{ext/strscan/extconf.rb ext/strscan/strscan.c}
- s.extensions = %w{ext/strscan/extconf.rb}
+ files = [
+ "COPYING",
+ "LICENSE.txt",
+ ]
+ if RUBY_ENGINE == "jruby"
+ s.require_paths = %w{ext/jruby/lib lib}
+ files << "ext/jruby/lib/strscan.rb"
+ files << "lib/strscan.jar"
+ s.platform = "java"
+ else
+ s.require_paths = %w{lib}
+ files << "ext/strscan/extconf.rb"
+ files << "ext/strscan/strscan.c"
+ s.extensions = %w{ext/strscan/extconf.rb}
+ end
+ s.files = files
s.required_ruby_version = ">= 2.4.0"
- s.authors = ["Minero Aoki", "Sutou Kouhei"]
- s.email = [nil, "kou@cozmixng.org"]
+ s.authors = ["Minero Aoki", "Sutou Kouhei", "Charles Oliver Nutter"]
+ s.email = [nil, "kou@cozmixng.org", "headius@headius.com"]
s.homepage = "https://github.com/ruby/strscan"
s.licenses = ["Ruby", "BSD-2-Clause"]
end
diff --git a/ext/syslog/syslog.gemspec b/ext/syslog/syslog.gemspec
index 8f73f5ad0d..6aa2e9570d 100644
--- a/ext/syslog/syslog.gemspec
+++ b/ext/syslog/syslog.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "syslog"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb
index b5b99ff684..bda8bb012f 100644
--- a/ext/win32/lib/win32/registry.rb
+++ b/ext/win32/lib/win32/registry.rb
@@ -69,7 +69,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
WCHAR_NUL = "\0".encode(WCHAR).freeze
WCHAR_CR = "\r".encode(WCHAR).freeze
WCHAR_SIZE = WCHAR_NUL.bytesize
- LOCALE = Encoding.find(Encoding.locale_charmap)
+ begin
+ LOCALE = Encoding.find(Encoding.locale_charmap)
+ rescue ArgumentError
+ LOCALE = Encoding::UTF_8
+ end
class Registry
@@ -740,14 +744,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
# method returns.
#
def write(name, type, data)
- termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
- data = data.encode(WCHAR)
- termsize = WCHAR_SIZE
+ data = data.encode(WCHAR) << WCHAR_NUL
when REG_MULTI_SZ
data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL
- termsize = WCHAR_SIZE
when REG_BINARY, REG_NONE
data = data.to_s
when REG_DWORD
@@ -759,7 +760,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
else
raise TypeError, "Unsupported type #{Registry.type2name(type)}"
end
- API.SetValue(@hkey, name, type, data, data.bytesize + termsize)
+ API.SetValue(@hkey, name, type, data, data.bytesize)
end
#
diff --git a/ext/win32ole/win32ole.gemspec b/ext/win32ole/win32ole.gemspec
index 977555c98d..b6ea8e8a55 100644
--- a/ext/win32ole/win32ole.gemspec
+++ b/ext/win32ole/win32ole.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "win32ole"
- spec.version = "1.8.8"
+ spec.version = "1.8.9"
spec.authors = ["Masaki Suketa"]
spec.email = ["suke@ruby-lang.org"]
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 20079a5d4c..aefdba0ebd 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -25,7 +25,7 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#define RUBY_ZLIB_VERSION "2.1.1"
+#define RUBY_ZLIB_VERSION "3.0.0"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
diff --git a/file.c b/file.c
index 5265d3a3a5..3a8439ef07 100644
--- a/file.c
+++ b/file.c
@@ -115,6 +115,8 @@ int flock(int, int);
# define link(f, t) rb_w32_ulink((f), (t))
# undef unlink
# define unlink(p) rb_w32_uunlink(p)
+# undef readlink
+# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
# undef rename
# define rename(f, t) rb_w32_urename((f), (t))
# undef symlink
@@ -268,6 +270,46 @@ rb_str_encode_ospath(VALUE path)
#ifdef __APPLE__
# define NORMALIZE_UTF8PATH 1
+
+# ifdef HAVE_WORKING_FORK
+static void
+rb_CFString_class_initialize_before_fork(void)
+{
+ /*
+ * Since macOS 13, CFString family API used in
+ * rb_str_append_normalized_ospath may internally use Objective-C classes
+ * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings.
+ *
+ * On the other hand, Objective-C classes should not be used for the first
+ * time in a fork()'ed but not exec()'ed process. Violations for this rule
+ * can result deadlock during class initialization, so Objective-C runtime
+ * conservatively crashes on such cases by default.
+ *
+ * Therefore, we need to use CFString API to initialize Objective-C classes
+ * used internally *before* fork().
+ *
+ * For future changes, please note that this initialization process cannot
+ * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled
+ * after CFStringInitializeTaggedStrings(), which is called during loading
+ * Objective-C runtime after ctor.
+ * For more details, see https://bugs.ruby-lang.org/issues/18912
+ */
+
+ /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */
+ const char small_str[] = "/";
+ long len = sizeof(small_str) - 1;
+
+ const CFAllocatorRef alloc = kCFAllocatorDefault;
+ CFStringRef s = CFStringCreateWithBytesNoCopy(alloc,
+ (const UInt8 *)small_str,
+ len, kCFStringEncodingUTF8,
+ FALSE, kCFAllocatorNull);
+ CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s);
+ CFRelease(m);
+ CFRelease(s);
+}
+# endif
+
static VALUE
rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
{
@@ -442,41 +484,6 @@ apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
return LONG2FIX(argc);
}
-/*
- * call-seq:
- * path -> filepath
- *
- * Returns the string filepath used to create +self+:
- *
- * f = File.new('t.txt') # => #<File:t.txt>
- f.path # => "t.txt"
- *
- * Does not normalize the returned filepath:
- *
- * f = File.new('../files/t.txt') # => #<File:../files/t.txt>
- f.path # => "../files/t.txt"
- *
- * Raises IOError for a file created using File::Constants::TMPFILE, because it has no filename.
- *
- * File#to_path is an alias for File#path.
- *
- */
-
-static VALUE
-rb_file_path(VALUE obj)
-{
- rb_io_t *fptr;
-
- fptr = RFILE(rb_io_taint_check(obj))->fptr;
- rb_io_check_initialized(fptr);
-
- if (NIL_P(fptr->pathv)) {
- rb_raise(rb_eIOError, "File is unnamed (TMPFILE?)");
- }
-
- return rb_str_dup(fptr->pathv);
-}
-
static size_t
stat_memsize(const void *p)
{
@@ -1723,8 +1730,8 @@ rb_file_socket_p(VALUE obj, VALUE fname)
if (rb_stat(fname, &st) < 0) return Qfalse;
if (S_ISSOCK(st.st_mode)) return Qtrue;
-
#endif
+
return Qfalse;
}
@@ -2517,7 +2524,7 @@ rb_file_birthtime(VALUE obj)
*
*/
-off_t
+rb_off_t
rb_file_size(VALUE file)
{
if (RB_TYPE_P(file, T_FILE)) {
@@ -2833,29 +2840,27 @@ utime_failed(struct apply_arg *aa)
#if defined(HAVE_UTIMES)
-# if defined(__APPLE__) && \
+# if !defined(HAVE_UTIMENSAT)
+/* utimensat() is not found, runtime check is not needed */
+# elif defined(__APPLE__) && \
(!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
# if defined(__has_attribute) && __has_attribute(availability)
typedef int utimensat_func(int, const char *, const struct timespec [2], int);
-RBIMPL_WARNING_PUSH();
-RBIMPL_WARNING_IGNORED(-Wunguarded-availability-new);
+RBIMPL_WARNING_PUSH()
+RBIMPL_WARNING_IGNORED(-Wunguarded-availability-new)
static inline utimensat_func *
rb_utimensat(void)
{
return &utimensat;
}
-RBIMPL_WARNING_POP();
+RBIMPL_WARNING_POP()
# define utimensat rb_utimensat()
# else /* __API_AVAILABLE macro does nothing on gcc */
__attribute__((weak)) int utimensat(int, const char *, const struct timespec [2], int);
# endif
-
-# define utimensat_available_p() (utimensat != NULL)
-# else
-# define utimensat_available_p() 1
# endif
static int
@@ -3114,7 +3119,6 @@ rb_file_s_readlink(VALUE klass, VALUE path)
return rb_readlink(path, rb_filesystem_encoding());
}
-#ifndef _WIN32
struct readlink_arg {
const char *path;
char *buf;
@@ -3170,7 +3174,6 @@ rb_readlink(VALUE path, rb_encoding *enc)
return v;
}
-#endif
#else
#define rb_file_s_readlink rb_f_notimplement
#endif
@@ -4500,9 +4503,9 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, rb_encoding *origenc, enum
rb_enc_associate(resolved, origenc);
}
- if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(resolved)) {
rb_enc_associate(resolved, rb_filesystem_encoding());
- if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(resolved)) {
rb_enc_associate(resolved, rb_ascii8bit_encoding());
}
}
@@ -4862,7 +4865,7 @@ rb_file_dirname_n(VALUE fname, int n)
* dotfile top 0
* end with dot dot 1
* .ext dot len of .ext
- * .ext:stream dot len of .ext without :stream (NT only)
+ * .ext:stream dot len of .ext without :stream (NTFS only)
*
*/
const char *
@@ -5086,41 +5089,17 @@ rb_file_s_join(VALUE klass, VALUE args)
return rb_file_join(args);
}
-#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
+#if defined(HAVE_TRUNCATE)
struct truncate_arg {
const char *path;
-#if defined(HAVE_TRUNCATE)
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ rb_off_t pos;
};
static void *
nogvl_truncate(void *ptr)
{
struct truncate_arg *ta = ptr;
-#ifdef HAVE_TRUNCATE
return (void *)(VALUE)truncate(ta->path, ta->pos);
-#else /* defined(HAVE_CHSIZE) */
- {
- int tmpfd = rb_cloexec_open(ta->path, 0, 0);
-
- if (tmpfd < 0)
- return (void *)-1;
- rb_update_max_fd(tmpfd);
- if (chsize(tmpfd, ta->pos) < 0) {
- int e = errno;
- close(tmpfd);
- errno = e;
- return (void *)-1;
- }
- close(tmpfd);
- return 0;
- }
-#endif
}
/*
@@ -5144,7 +5123,7 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
struct truncate_arg ta;
int r;
- ta.pos = NUM2POS(len);
+ ta.pos = NUM2OFFT(len);
FilePathValue(path);
path = rb_str_encode_ospath(path);
ta.path = StringValueCStr(path);
@@ -5154,22 +5133,15 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
if (r < 0)
rb_sys_fail_path(path);
return INT2FIX(0);
-#undef NUM2POS
}
#else
#define rb_file_s_truncate rb_f_notimplement
#endif
-#if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
+#if defined(HAVE_FTRUNCATE)
struct ftruncate_arg {
int fd;
-#if defined(HAVE_FTRUNCATE)
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ rb_off_t pos;
};
static VALUE
@@ -5177,11 +5149,7 @@ nogvl_ftruncate(void *ptr)
{
struct ftruncate_arg *fa = ptr;
-#ifdef HAVE_FTRUNCATE
return (VALUE)ftruncate(fa->fd, fa->pos);
-#else /* defined(HAVE_CHSIZE) */
- return (VALUE)chsize(fa->fd, fa->pos);
-#endif
}
/*
@@ -5204,7 +5172,7 @@ rb_file_truncate(VALUE obj, VALUE len)
rb_io_t *fptr;
struct ftruncate_arg fa;
- fa.pos = NUM2POS(len);
+ fa.pos = NUM2OFFT(len);
GetOpenFile(obj, fptr);
if (!(fptr->mode & FMODE_WRITABLE)) {
rb_raise(rb_eIOError, "not opened for writing");
@@ -5215,7 +5183,6 @@ rb_file_truncate(VALUE obj, VALUE len)
rb_sys_fail_path(fptr->pathv);
}
return INT2FIX(0);
-#undef NUM2POS
}
#else
#define rb_file_truncate rb_f_notimplement
@@ -5375,8 +5342,7 @@ test_check(int n, int argc, VALUE *argv)
* "d" | boolean | True if file1 exists and is a directory
* "e" | boolean | True if file1 exists
* "f" | boolean | True if file1 exists and is a regular file
- * "g" | boolean | True if file1 has the \CF{setgid} bit
- * | | set (false under NT)
+ * "g" | boolean | True if file1 has the setgid bit set
* "G" | boolean | True if file1 exists and has a group
* | | ownership equal to the caller's group
* "k" | boolean | True if file1 exists and has the sticky bit set
@@ -5599,6 +5565,7 @@ rb_stat_init(VALUE obj, VALUE fname)
if (STAT(StringValueCStr(fname), &st) == -1) {
rb_sys_fail_path(fname);
}
+
if (DATA_PTR(obj)) {
xfree(DATA_PTR(obj));
DATA_PTR(obj) = NULL;
@@ -5807,7 +5774,7 @@ rb_stat_rowned(VALUE obj)
* stat.grpowned? -> true or false
*
* Returns true if the effective group id of the process is the same as
- * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
+ * the group id of <i>stat</i>. On Windows, returns <code>false</code>.
*
* File.stat("testfile").grpowned? #=> true
* File.stat("/etc/passwd").grpowned? #=> false
@@ -6129,7 +6096,7 @@ rb_stat_z(VALUE obj)
static VALUE
rb_stat_s(VALUE obj)
{
- off_t size = get_stat(obj)->st_size;
+ rb_off_t size = get_stat(obj)->st_size;
if (size == 0) return Qnil;
return OFFT2NUM(size);
@@ -6540,12 +6507,10 @@ rb_find_file(VALUE path)
return copy_path_class(tmp, path);
}
-static void
-define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
-{
- rb_define_module_function(rb_mFileTest, name, func, argc);
- rb_define_singleton_method(rb_cFile, name, func, argc);
-}
+#define define_filetest_function(name, func, argc) do { \
+ rb_define_module_function(rb_mFileTest, name, func, argc); \
+ rb_define_singleton_method(rb_cFile, name, func, argc); \
+} while(false)
const char ruby_null_device[] =
#if defined DOSISH
@@ -6565,6 +6530,602 @@ const char ruby_null_device[] =
* \Class \File extends module FileTest, supporting such singleton methods
* as <tt>File.exist?</tt>.
*
+ * === About the Examples
+ *
+ * Many examples here use these variables:
+ *
+ * :include: doc/examples/files.rdoc
+ *
+ * == Access Modes
+ *
+ * \Methods File.new and File.open each create a \File object for a given file path.
+ *
+ * === \String Access Modes
+ *
+ * \Methods File.new and File.open each may take string argument +mode+, which:
+ *
+ * - Begins with a 1- or 2-character
+ * {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode].
+ * - May also contain a 1-character {data mode}[rdoc-ref:File@Data+Mode].
+ * - May also contain a 1-character
+ * {file-create mode}[rdoc-ref:File@File-Create+Mode].
+ *
+ * ==== Read/Write Mode
+ *
+ * The read/write +mode+ determines:
+ *
+ * - Whether the file is to be initially truncated.
+ *
+ * - Whether reading is allowed, and if so:
+ *
+ * - The initial read position in the file.
+ * - Where in the file reading can occur.
+ *
+ * - Whether writing is allowed, and if so:
+ *
+ * - The initial write position in the file.
+ * - Where in the file writing can occur.
+ *
+ * These tables summarize:
+ *
+ * Read/Write Modes for Existing File
+ *
+ * |------|-----------|----------|----------|----------|-----------|
+ * | R/W | Initial | | Initial | | Initial |
+ * | Mode | Truncate? | Read | Read Pos | Write | Write Pos |
+ * |------|-----------|----------|----------|----------|-----------|
+ * | 'r' | No | Anywhere | 0 | Error | - |
+ * | 'w' | Yes | Error | - | Anywhere | 0 |
+ * | 'a' | No | Error | - | End only | End |
+ * | 'r+' | No | Anywhere | 0 | Anywhere | 0 |
+ * | 'w+' | Yes | Anywhere | 0 | Anywhere | 0 |
+ * | 'a+' | No | Anywhere | End | End only | End |
+ * |------|-----------|----------|----------|----------|-----------|
+ *
+ * Read/Write Modes for \File To Be Created
+ *
+ * |------|----------|----------|----------|-----------|
+ * | R/W | | Initial | | Initial |
+ * | Mode | Read | Read Pos | Write | Write Pos |
+ * |------|----------|----------|----------|-----------|
+ * | 'w' | Error | - | Anywhere | 0 |
+ * | 'a' | Error | - | End only | 0 |
+ * | 'w+' | Anywhere | 0 | Anywhere | 0 |
+ * | 'a+' | Anywhere | 0 | End only | End |
+ * |------|----------|----------|----------|-----------|
+ *
+ * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
+ * for a non-existent file (exception raised).
+ *
+ * In the tables:
+ *
+ * - +Anywhere+ means that methods IO#rewind, IO#pos=, and IO#seek
+ * may be used to change the file's position,
+ * so that allowed reading or writing may occur anywhere in the file.
+ * - <tt>End only</tt> means that writing can occur only at end-of-file,
+ * and that methods IO#rewind, IO#pos=, and IO#seek do not affect writing.
+ * - +Error+ means that an exception is raised if disallowed reading or writing
+ * is attempted.
+ *
+ * ===== Read/Write Modes for Existing \File
+ *
+ * - <tt>'r'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * f = File.new('t.txt') # => #<File:t.txt>
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.readline # => "First line\n"
+ * f.readline # => "Second line\n"
+ *
+ * f.rewind
+ * f.readline # => "First line\n"
+ *
+ * f.pos = 1
+ * f.readline # => "irst line\n"
+ *
+ * f.seek(1, :CUR)
+ * f.readline # => "econd line\n"
+ *
+ * - Writing is not allowed:
+ *
+ * f.write('foo') # Raises IOError.
+ *
+ * - <tt>'w'</tt>:
+ *
+ * - File is initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, text)
+ * f = File.new(path, 'w')
+ * f.size == 0 # => true
+ *
+ * - File's initial write position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'a'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, 'foo')
+ * f = File.new(path, 'a')
+ * f.size == 0 # => false
+ *
+ * - File's initial position is 0 (but is ignored):
+ *
+ * f.pos # => 0
+ *
+ * - File may be written only at end-of-file;
+ * IO#rewind, IO#pos=, IO#seek do not affect writing:
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * f.rewind
+ * f.write('bat')
+ * f.flush
+ * File.read(path) # => "foobarbazbat"
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'r+'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, text)
+ * f = File.new(path, 'r+')
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be read or written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.readline # => "First line\n"
+ * f.readline # => "Second line\n"
+ *
+ * f.rewind
+ * f.readline # => "First line\n"
+ *
+ * f.pos = 1
+ * f.readline # => "irst line\n"
+ *
+ * f.seek(1, :CUR)
+ * f.readline # => "econd line\n"
+ *
+ * f.rewind
+ * f.write('WWW')
+ * f.flush
+ * File.read(path)
+ * # => "WWWst line\nSecond line\nFourth line\nFifth line\n"
+ *
+ * f.pos = 10
+ * f.write('XXX')
+ * f.flush
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth line\n"
+ *
+ * f.seek(-6, :END)
+ * # => 0
+ * f.write('YYY')
+ * # => 3
+ * f.flush
+ * # => #<File:t.tmp>
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n"
+ *
+ * f.seek(2, :END)
+ * f.write('ZZZ') # Zero padding as needed.
+ * f.flush
+ * File.read(path)
+ * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n\u0000\u0000ZZZ"
+ *
+ *
+ * - <tt>'a+'</tt>:
+ *
+ * - File is not initially truncated:
+ *
+ * path = 't.tmp'
+ * File.write(path, 'foo')
+ * f = File.new(path, 'a+')
+ * f.size == 0 # => false
+ *
+ * - File's initial read position is 0:
+ *
+ * f.pos # => 0
+ *
+ * - File may be written only at end-of-file;
+ * IO#rewind, IO#pos=, IO#seek do not affect writing:
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * f.rewind
+ * f.write('bat')
+ * f.flush
+ * File.read(path) # => "foobarbazbat"
+ *
+ * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * f.read # => "foobarbazbat"
+ *
+ * f.pos = 3
+ * f.read # => "barbazbat"
+ *
+ * f.seek(-3, :END)
+ * f.read # => "bat"
+ *
+ * ===== Read/Write Modes for \File To Be Created
+ *
+ * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
+ * for a non-existent file (exception raised).
+ *
+ * - <tt>'w'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'w')
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'a'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'a')
+ * f.pos # => 0
+ *
+ * - Writing occurs only at end-of-file:
+ *
+ * f.write('foo')
+ * f.pos # => 3
+ * f.write('bar')
+ * f.pos # => 6
+ * f.flush
+ * File.read(path) # => "foobar"
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * - Reading is not allowed:
+ *
+ * f.read # Raises IOError.
+ *
+ * - <tt>'w+'</tt>:
+ *
+ * - File's initial position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'w+')
+ * f.pos # => 0
+ *
+ * - File may be written anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "foo"
+ * f.pos # => 3
+ *
+ * f.write('bar')
+ * f.flush
+ * File.read(path) # => "foobar"
+ * f.pos # => 6
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "bazbar"
+ * f.pos # => 3
+ *
+ * f.pos = 3
+ * f.write('foo')
+ * f.flush
+ * File.read(path) # => "bazfoo"
+ * f.pos # => 6
+ *
+ * f.seek(-3, :END)
+ * f.write('bam')
+ * f.flush
+ * File.read(path) # => "bazbam"
+ * f.pos # => 6
+ *
+ * f.pos = 8
+ * f.write('bah') # Zero padding as needed.
+ * f.flush
+ * File.read(path) # => "bazbam\u0000\u0000bah"
+ * f.pos # => 11
+ *
+ * - File may be read anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * # => 0
+ * f.read
+ * # => "bazbam\u0000\u0000bah"
+ *
+ * f.pos = 3
+ * # => 3
+ * f.read
+ * # => "bam\u0000\u0000bah"
+ *
+ * f.seek(-3, :END)
+ * # => 0
+ * f.read
+ * # => "bah"
+ *
+ * - <tt>'a+'</tt>:
+ *
+ * - File's initial write position is 0:
+ *
+ * path = 't.tmp'
+ * FileUtils.rm_f(path)
+ * f = File.new(path, 'a+')
+ * f.pos # => 0
+ *
+ * - Writing occurs only at end-of-file:
+ *
+ * f.write('foo')
+ * f.pos # => 3
+ * f.write('bar')
+ * f.pos # => 6
+ * f.flush
+ * File.read(path) # => "foobar"
+ *
+ * f.rewind
+ * f.write('baz')
+ * f.flush
+ * File.read(path) # => "foobarbaz"
+ *
+ * - File may be read anywhere (even past end-of-file);
+ * see IO#rewind, IO#pos=, IO#seek:
+ *
+ * f.rewind
+ * f.read # => "foobarbaz"
+ *
+ * f.pos = 3
+ * f.read # => "barbaz"
+ *
+ * f.seek(-3, :END)
+ * f.read # => "baz"
+ *
+ * f.pos = 800
+ * f.read # => ""
+ *
+ * ==== \Data Mode
+ *
+ * To specify whether data is to be treated as text or as binary data,
+ * either of the following may be suffixed to any of the string read/write modes
+ * above:
+ *
+ * - <tt>'t'</tt>: Text data; sets the default external encoding
+ * to <tt>Encoding::UTF_8</tt>;
+ * on Windows, enables conversion between EOL and CRLF
+ * and enables interpreting <tt>0x1A</tt> as an end-of-file marker.
+ * - <tt>'b'</tt>: Binary data; sets the default external encoding
+ * to <tt>Encoding::ASCII_8BIT</tt>;
+ * on Windows, suppresses conversion between EOL and CRLF
+ * and disables interpreting <tt>0x1A</tt> as an end-of-file marker.
+ *
+ * If neither is given, the stream defaults to text data.
+ *
+ * Examples:
+ *
+ * File.new('t.txt', 'rt')
+ * File.new('t.dat', 'rb')
+ *
+ * When the data mode is specified, the read/write mode may not be omitted,
+ * and the data mode must precede the file-create mode, if given:
+ *
+ * File.new('t.dat', 'b') # Raises an exception.
+ * File.new('t.dat', 'rxb') # Raises an exception.
+ *
+ * ==== \File-Create Mode
+ *
+ * The following may be suffixed to any writable string mode above:
+ *
+ * - <tt>'x'</tt>: Creates the file if it does not exist;
+ * raises an exception if the file exists.
+ *
+ * Example:
+ *
+ * File.new('t.tmp', 'wx')
+ *
+ * When the file-create mode is specified, the read/write mode may not be omitted,
+ * and the file-create mode must follow the data mode:
+ *
+ * File.new('t.dat', 'x') # Raises an exception.
+ * File.new('t.dat', 'rxb') # Raises an exception.
+ *
+ * === \Integer Access Modes
+ *
+ * When mode is an integer it must be one or more of the following constants,
+ * which may be combined by the bitwise OR operator <tt>|</tt>:
+ *
+ * - +File::RDONLY+: Open for reading only.
+ * - +File::WRONLY+: Open for writing only.
+ * - +File::RDWR+: Open for reading and writing.
+ * - +File::APPEND+: Open for appending only.
+ *
+ * Examples:
+ *
+ * File.new('t.txt', File::RDONLY)
+ * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
+ *
+ * Note: Method IO#set_encoding does not allow the mode to be specified as an integer.
+ *
+ * === File-Create Mode Specified as an \Integer
+ *
+ * These constants may also be ORed into the integer mode:
+ *
+ * - +File::CREAT+: Create file if it does not exist.
+ * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists.
+ *
+ * === \Data Mode Specified as an \Integer
+ *
+ * \Data mode cannot be specified as an integer.
+ * When the stream access mode is given as an integer,
+ * the data mode is always text, never binary.
+ *
+ * Note that although there is a constant +File::BINARY+,
+ * setting its value in an integer stream mode has no effect;
+ * this is because, as documented in File::Constants,
+ * the +File::BINARY+ value disables line code conversion,
+ * but does not change the external encoding.
+ *
+ * === Encodings
+ *
+ * Any of the string modes above may specify encodings -
+ * either external encoding only or both external and internal encodings -
+ * by appending one or both encoding names, separated by colons:
+ *
+ * f = File.new('t.dat', 'rb')
+ * f.external_encoding # => #<Encoding:ASCII-8BIT>
+ * f.internal_encoding # => nil
+ * f = File.new('t.dat', 'rb:UTF-16')
+ * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
+ * f.internal_encoding # => nil
+ * f = File.new('t.dat', 'rb:UTF-16:UTF-16')
+ * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
+ * f.internal_encoding # => #<Encoding:UTF-16>
+ * f.close
+ *
+ * The numerous encoding names are available in array Encoding.name_list:
+ *
+ * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
+ *
+ * When the external encoding is set, strings read are tagged by that encoding
+ * when reading, and strings written are converted to that encoding when
+ * writing.
+ *
+ * When both external and internal encodings are set,
+ * strings read are converted from external to internal encoding,
+ * and strings written are converted from internal to external encoding.
+ * For further details about transcoding input and output,
+ * see {Encodings}[rdoc-ref:encodings.rdoc@Encodings].
+ *
+ * If the external encoding is <tt>'BOM|UTF-8'</tt>, <tt>'BOM|UTF-16LE'</tt>
+ * or <tt>'BOM|UTF16-BE'</tt>,
+ * Ruby checks for a Unicode BOM in the input document
+ * to help determine the encoding.
+ * For UTF-16 encodings the file open mode must be binary.
+ * If the BOM is found,
+ * it is stripped and the external encoding from the BOM is used.
+ *
+ * Note that the BOM-style encoding option is case insensitive,
+ * so <tt>'bom|utf-8'</tt> is also valid.
+ *
* == \File Permissions
*
* A \File object has _permissions_, an octal integer representing
@@ -6616,40 +7177,12 @@ const char ruby_null_device[] =
* f.chmod(0644)
* f.chmod(0444)
*
- * == \File Constants
+ * == \File \Constants
*
* Various constants for use in \File and \IO methods
* may be found in module File::Constants;
* an array of their names is returned by <tt>File::Constants.constants</tt>.
*
- * == Example Files
- *
- * Many examples here use these filenames and their corresponding files:
- *
- * - <tt>t.txt</tt>: A text-only file that is assumed to exist via:
- *
- * text = <<~EOT
- * First line
- * Second line
- *
- * Fourth line
- * Fifth line
- * EOT
- * File.write('t.txt', text)
- *
- * - <tt>t.dat</tt>: A data file that is assumed to exist via:
- *
- * data = "\u9990\u9991\u9992\u9993\u9994"
- * f = File.open('t.dat', 'wb:UTF-16')
- * f.write(data)
- * f.close
- *
- * - <tt>t.rus</tt>: A Russian-language text file that is assumed to exist via:
- *
- * File.write('t.rus', "\u{442 435 441 442}")
- *
- * - <tt>t.tmp</tt>: A file that is assumed _not_ to exist.
- *
* == What's Here
*
* First, what's elsewhere. \Class \File:
@@ -6716,7 +7249,7 @@ const char ruby_null_device[] =
*
* - ::blockdev?: Returns whether the file at the given path is a block device.
* - ::chardev?: Returns whether the file at the given path is a character device.
- * - ::directory?: Returns whether the file at the given path is a diretory.
+ * - ::directory?: Returns whether the file at the given path is a directory.
* - ::executable?: Returns whether the file at the given path is executable
* by the effective user and group of the current process.
* - ::executable_real?: Returns whether the file at the given path is executable
@@ -6787,6 +7320,10 @@ const char ruby_null_device[] =
void
Init_File(void)
{
+#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
+ rb_CFString_class_initialize_before_fork();
+#endif
+
VALUE separator;
rb_mFileTest = rb_define_module("FileTest");
@@ -6983,8 +7520,6 @@ Init_File(void)
/* Name of the null device */
rb_define_const(rb_mFConst, "NULL", rb_fstring_cstr(ruby_null_device));
- rb_define_method(rb_cFile, "path", rb_file_path, 0);
- rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
rb_define_global_function("test", rb_f_test, -1);
rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
diff --git a/gc.c b/gc.c
index ecb4aa7e20..919d57989a 100644
--- a/gc.c
+++ b/gc.c
@@ -138,6 +138,7 @@
#include "ractor_core.h"
#include "builtin.h"
+#include "shape.h"
#define rb_setjmp(env) RUBY_SETJMP(env)
#define rb_jmp_buf rb_jmpbuf_t
@@ -573,6 +574,7 @@ struct RMoved {
VALUE flags;
VALUE dummy;
VALUE destination;
+ shape_id_t original_shape_id;
};
#define RMOVED(obj) ((struct RMoved *)(obj))
@@ -620,17 +622,29 @@ typedef struct RVALUE {
VALUE v3;
} values;
} as;
+
+ /* Start of RVALUE_OVERHEAD.
+ * Do not directly read these members from the RVALUE as they're located
+ * at the end of the slot (which may differ in size depending on the size
+ * pool). */
+#if RACTOR_CHECK_MODE
+ uint32_t _ractor_belonging_id;
+#endif
#if GC_DEBUG
const char *file;
int line;
#endif
} RVALUE;
-#if GC_DEBUG
-STATIC_ASSERT(sizeof_rvalue, offsetof(RVALUE, file) == SIZEOF_VALUE * 5);
+#if RACTOR_CHECK_MODE
+# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, _ractor_belonging_id))
+#elif GC_DEBUG
+# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, file))
#else
-STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == SIZEOF_VALUE * 5);
+# define RVALUE_OVERHEAD 0
#endif
+
+STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == (SIZEOF_VALUE * 5) + RVALUE_OVERHEAD);
STATIC_ASSERT(alignof_rvalue, RUBY_ALIGNOF(RVALUE) == SIZEOF_VALUE);
typedef uintptr_t bits_t;
@@ -870,7 +884,7 @@ typedef struct rb_objspace {
#define BASE_SLOT_SIZE sizeof(RVALUE)
-#define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod))
+#define CEILDIV(i, mod) roomof(i, mod)
enum {
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG),
HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)),
@@ -932,7 +946,6 @@ struct heap_page {
short slot_size;
short total_slots;
short free_slots;
- short pinned_slots;
short final_slots;
struct {
unsigned int before_sweep : 1;
@@ -1359,6 +1372,27 @@ tick(void)
return val;
}
+/* Implementation for macOS PPC by @nobu
+ * See: https://github.com/ruby/ruby/pull/5975#discussion_r890045558
+ */
+#elif defined(__POWERPC__) && defined(__APPLE__)
+typedef unsigned long long tick_t;
+#define PRItick "llu"
+
+static __inline__ tick_t
+tick(void)
+{
+ unsigned long int upper, lower, tmp;
+ # define mftbu(r) __asm__ volatile("mftbu %0" : "=r"(r))
+ # define mftb(r) __asm__ volatile("mftb %0" : "=r"(r))
+ do {
+ mftbu(upper);
+ mftb(lower);
+ mftbu(tmp);
+ } while (tmp != upper);
+ return ((tick_t)upper << 32) | lower;
+}
+
#elif defined(__aarch64__) && defined(__GNUC__)
typedef unsigned long tick_t;
#define PRItick "lu"
@@ -2440,6 +2474,7 @@ rb_objspace_set_event_hook(const rb_event_flag_t event)
static void
gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
{
+ if (UNLIKELY(!ec->cfp)) return;
const VALUE *pc = ec->cfp->pc;
if (pc && VM_FRAME_RUBYFRAME_P(ec->cfp)) {
/* increment PC because source line is calculated with PC-1 */
@@ -2554,7 +2589,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
size_t
rb_gc_obj_slot_size(VALUE obj)
{
- return GET_HEAP_PAGE(obj)->slot_size;
+ return GET_HEAP_PAGE(obj)->slot_size - RVALUE_OVERHEAD;
}
static inline size_t
@@ -2569,9 +2604,17 @@ size_pool_slot_size(unsigned char pool_id)
GC_ASSERT(size_pools[pool_id].slot_size == (short)slot_size);
#endif
+ slot_size -= RVALUE_OVERHEAD;
+
return slot_size;
}
+size_t
+rb_size_pool_slot_size(unsigned char pool_id)
+{
+ return size_pool_slot_size(pool_id);
+}
+
bool
rb_gc_size_allocatable_p(size_t size)
{
@@ -2677,6 +2720,8 @@ static inline size_t
size_pool_idx_for_size(size_t size)
{
#if USE_RVARGC
+ size += RVALUE_OVERHEAD;
+
size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE);
/* size_pool_idx is ceil(log2(slot_count)) */
@@ -2751,6 +2796,12 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, boo
return obj;
}
+static void
+newobj_zero_slot(VALUE obj)
+{
+ memset((char *)obj + sizeof(struct RBasic), 0, rb_gc_obj_slot_size(obj) - sizeof(struct RBasic));
+}
+
ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx));
static inline VALUE
@@ -2776,9 +2827,12 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
obj = newobj_alloc(objspace, cr, size_pool_idx, true);
+#if SHAPE_IN_BASIC_FLAGS
+ flags |= (VALUE)(size_pool_idx) << SHAPE_FLAG_SHIFT;
+#endif
newobj_init(klass, flags, wb_protected, objspace, obj);
- gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_fill(obj, 0, 0, 0));
+ gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj));
}
RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev);
@@ -2827,6 +2881,9 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a
gc_event_hook_available_p(objspace)) &&
wb_protected) {
obj = newobj_alloc(objspace, cr, size_pool_idx, false);
+#if SHAPE_IN_BASIC_FLAGS
+ flags |= (VALUE)size_pool_idx << SHAPE_FLAG_SHIFT;
+#endif
newobj_init(klass, flags, wb_protected, objspace, obj);
}
else {
@@ -2880,7 +2937,7 @@ rb_ec_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flag
VALUE
rb_newobj(void)
{
- return newobj_of(0, T_NONE, 0, 0, 0, FALSE, sizeof(RVALUE));
+ return newobj_of(0, T_NONE, 0, 0, 0, FALSE, RVALUE_SIZE);
}
static size_t
@@ -2895,44 +2952,33 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected)
GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT);
GC_ASSERT(flags & ROBJECT_EMBED);
- st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass);
- uint32_t index_tbl_num_entries = index_tbl == NULL ? 0 : (uint32_t)index_tbl->num_entries;
-
size_t size;
- bool embed = true;
#if USE_RVARGC
+ uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count;
+
size = rb_obj_embedded_size(index_tbl_num_entries);
if (!rb_gc_size_allocatable_p(size)) {
size = sizeof(struct RObject);
- embed = false;
}
#else
size = sizeof(struct RObject);
- if (index_tbl_num_entries > ROBJECT_EMBED_LEN_MAX) {
- embed = false;
- }
#endif
-#if USE_RVARGC
VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size);
-#else
- VALUE obj = newobj_of(klass, flags, Qundef, Qundef, Qundef, wb_protected, size);
-#endif
+ RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT ||
+ rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY);
- if (embed) {
-#if USE_RVARGC
- uint32_t capa = (uint32_t)((rb_gc_obj_slot_size(obj) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
- GC_ASSERT(capa >= index_tbl_num_entries);
+ // Set the shape to the specific T_OBJECT shape which is always
+ // SIZE_POOL_COUNT away from the root shape.
+ ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT);
- ROBJECT(obj)->numiv = capa;
- for (size_t i = 0; i < capa; i++) {
- ROBJECT(obj)->as.ary[i] = Qundef;
- }
-#endif
- }
- else {
- rb_init_iv_list(obj);
+#if RUBY_DEBUG
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ for (size_t i = 0; i < ROBJECT_IV_CAPACITY(obj); i++) {
+ ptr[i] = Qundef;
}
+#endif
return obj;
}
@@ -2944,7 +2990,7 @@ rb_newobj_of(VALUE klass, VALUE flags)
return rb_class_instance_allocate_internal(klass, (flags | ROBJECT_EMBED) & ~FL_WB_PROTECTED, flags & FL_WB_PROTECTED);
}
else {
- return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, sizeof(RVALUE));
+ return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, RVALUE_SIZE);
}
}
@@ -2982,7 +3028,7 @@ rb_imemo_name(enum imemo_type type)
VALUE
rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
{
- size_t size = sizeof(RVALUE);
+ size_t size = RVALUE_SIZE;
VALUE flags = T_IMEMO | (type << FL_USHIFT);
return newobj_of(v0, flags, v1, v2, v3, TRUE, size);
}
@@ -2990,7 +3036,7 @@ rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
static VALUE
rb_imemo_tmpbuf_new(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
{
- size_t size = sizeof(RVALUE);
+ size_t size = sizeof(struct rb_imemo_tmpbuf_struct);
VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT);
return newobj_of(v0, flags, v1, v2, v3, FALSE, size);
}
@@ -3051,7 +3097,7 @@ rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0,
}
#endif
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_class_allocate_instance(VALUE klass)
{
return rb_class_instance_allocate_internal(klass, T_OBJECT | ROBJECT_EMBED, RGENGC_WB_PROTECTED_OBJECT);
@@ -3071,7 +3117,7 @@ rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FU
{
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
if (klass) rb_data_object_check(klass);
- return newobj_of(klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, FALSE, sizeof(RVALUE));
+ return newobj_of(klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, FALSE, sizeof(struct RTypedData));
}
VALUE
@@ -3087,7 +3133,7 @@ rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
{
RBIMPL_NONNULL_ARG(type);
if (klass) rb_data_object_check(klass);
- return newobj_of(klass, T_DATA, (VALUE)type, (VALUE)1, (VALUE)datap, type->flags & RUBY_FL_WB_PROTECTED, sizeof(RVALUE));
+ return newobj_of(klass, T_DATA, (VALUE)type, (VALUE)1, (VALUE)datap, type->flags & RUBY_FL_WB_PROTECTED, sizeof(struct RTypedData));
}
VALUE
@@ -3206,20 +3252,6 @@ rb_free_const_table(struct rb_id_table *tbl)
rb_id_table_free(tbl);
}
-static int
-free_iv_index_tbl_free_i(st_data_t key, st_data_t value, st_data_t data)
-{
- xfree((void *)value);
- return ST_CONTINUE;
-}
-
-static void
-iv_index_tbl_free(struct st_table *tbl)
-{
- st_foreach(tbl, free_iv_index_tbl_free_i, 0);
- st_free_table(tbl);
-}
-
// alive: if false, target pointers can be freed already.
// To check it, we need objspace parameter.
static void
@@ -3428,7 +3460,11 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
+ if (rb_shape_obj_too_complex(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_obj_too_complex);
+ st_free_table(ROBJECT_IV_HASH(obj));
+ }
+ else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
RB_DEBUG_COUNTER_INC(obj_obj_embed);
}
else if (ROBJ_TRANSIENT_P(obj)) {
@@ -3443,15 +3479,12 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case T_CLASS:
rb_id_table_free(RCLASS_M_TBL(obj));
cc_table_free(objspace, obj, FALSE);
- if (RCLASS_IV_TBL(obj)) {
- st_free_table(RCLASS_IV_TBL(obj));
+ if (RCLASS_IVPTR(obj)) {
+ xfree(RCLASS_IVPTR(obj));
}
if (RCLASS_CONST_TBL(obj)) {
rb_free_const_table(RCLASS_CONST_TBL(obj));
}
- if (RCLASS_IV_INDEX_TBL(obj)) {
- iv_index_tbl_free(RCLASS_IV_INDEX_TBL(obj));
- }
if (RCLASS_CVC_TBL(obj)) {
rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL);
rb_id_table_free(RCLASS_CVC_TBL(obj));
@@ -3462,11 +3495,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
xfree(RCLASS_SUPERCLASSES(obj));
}
-#if SIZEOF_SERIAL_T != SIZEOF_VALUE && USE_RVARGC
- xfree(RCLASS(obj)->class_serial_ptr);
-#endif
-#if !USE_RVARGC
+#if SIZE_POOL_COUNT == 1
if (RCLASS_EXT(obj))
xfree(RCLASS_EXT(obj));
#endif
@@ -3632,7 +3662,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
cc_table_free(objspace, obj, FALSE);
rb_class_remove_from_module_subclasses(obj);
rb_class_remove_from_super_subclasses(obj);
-#if !USE_RVARGC
+#if !RCLASS_EXT_EMBEDDED
xfree(RCLASS_EXT(obj));
#endif
@@ -4311,7 +4341,7 @@ run_single_final(VALUE cmd, VALUE objid)
static void
warn_exception_in_finalizer(rb_execution_context_t *ec, VALUE final)
{
- if (final != Qundef && !NIL_P(ruby_verbose)) {
+ if (!UNDEF_P(final) && !NIL_P(ruby_verbose)) {
VALUE errinfo = ec->errinfo;
rb_warn("Exception in finalizer %+"PRIsVALUE, final);
rb_ec_error_print(ec, errinfo);
@@ -4328,16 +4358,20 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
VALUE objid;
VALUE final;
rb_control_frame_t *cfp;
+ VALUE *sp;
long finished;
} saved;
+
rb_execution_context_t * volatile ec = GET_EC();
#define RESTORE_FINALIZER() (\
ec->cfp = saved.cfp, \
+ ec->cfp->sp = saved.sp, \
ec->errinfo = saved.errinfo)
saved.errinfo = ec->errinfo;
saved.objid = rb_obj_id(obj);
saved.cfp = ec->cfp;
+ saved.sp = ec->cfp->sp;
saved.finished = 0;
saved.final = Qundef;
@@ -4677,7 +4711,7 @@ id2ref(VALUE objid)
}
}
- if ((orig = id2ref_obj_tbl(objspace, objid)) != Qundef &&
+ if (!UNDEF_P(orig = id2ref_obj_tbl(objspace, objid)) &&
is_live_object(objspace, orig)) {
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(orig)) {
@@ -4696,6 +4730,7 @@ id2ref(VALUE objid)
}
}
+/* :nodoc: */
static VALUE
os_id2ref(VALUE os, VALUE objid)
{
@@ -4857,8 +4892,11 @@ obj_memsize_of(VALUE obj, int use_all_types)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
- size += ROBJECT_NUMIV(obj) * sizeof(VALUE);
+ if (rb_shape_obj_too_complex(obj)) {
+ size += rb_st_memsize(ROBJECT_IV_HASH(obj));
+ }
+ else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
+ size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE);
}
break;
case T_MODULE:
@@ -4867,19 +4905,11 @@ obj_memsize_of(VALUE obj, int use_all_types)
if (RCLASS_M_TBL(obj)) {
size += rb_id_table_memsize(RCLASS_M_TBL(obj));
}
- if (RCLASS_IV_TBL(obj)) {
- size += st_memsize(RCLASS_IV_TBL(obj));
- }
+ // class IV sizes are allocated as powers of two
+ size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj));
if (RCLASS_CVC_TBL(obj)) {
size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
}
- if (RCLASS_IV_INDEX_TBL(obj)) {
- // TODO: more correct value
- size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
- }
- if (RCLASS_EXT(obj)->iv_tbl) {
- size += st_memsize(RCLASS_EXT(obj)->iv_tbl);
- }
if (RCLASS_EXT(obj)->const_tbl) {
size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl);
}
@@ -4889,7 +4919,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE);
}
-#if !USE_RVARGC
+#if SIZE_POOL_COUNT == 1
size += sizeof(rb_classext_t);
#endif
}
@@ -4980,7 +5010,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
BUILTIN_TYPE(obj), (void*)obj);
}
- return size + GET_HEAP_PAGE(obj)->slot_size;
+ return size + rb_gc_obj_slot_size(obj);
}
size_t
@@ -5212,6 +5242,8 @@ unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body)
static bool
try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src)
{
+ GC_ASSERT(gc_is_moveable_obj(objspace, src));
+
struct heap_page *src_page = GET_HEAP_PAGE(src);
if (!free_page) {
return false;
@@ -5220,33 +5252,33 @@ try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page,
/* We should return true if either src is successfully moved, or src is
* unmoveable. A false return will cause the sweeping cursor to be
* incremented to the next page, and src will attempt to move again */
- if (gc_is_moveable_obj(objspace, src)) {
- GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src));
-
- VALUE dest = (VALUE)free_page->freelist;
- asan_unpoison_object(dest, false);
- if (!dest) {
- /* if we can't get something from the freelist then the page must be
- * full */
- return false;
- }
- free_page->freelist = RANY(dest)->as.free.next;
-
- GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE);
+ GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src));
+
+ asan_unlock_freelist(free_page);
+ VALUE dest = (VALUE)free_page->freelist;
+ asan_lock_freelist(free_page);
+ asan_unpoison_object(dest, false);
+ if (!dest) {
+ /* if we can't get something from the freelist then the page must be
+ * full */
+ return false;
+ }
+ free_page->freelist = RANY(dest)->as.free.next;
- if (src_page->slot_size > free_page->slot_size) {
- objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++;
- }
- else if (free_page->slot_size > src_page->slot_size) {
- objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++;
- }
- objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++;
- objspace->rcompactor.total_moved++;
+ GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE);
- gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size);
- gc_pin(objspace, src);
- free_page->free_slots--;
+ if (src_page->slot_size > free_page->slot_size) {
+ objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++;
}
+ else if (free_page->slot_size > src_page->slot_size) {
+ objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++;
+ }
+ objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++;
+ objspace->rcompactor.total_moved++;
+
+ gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size);
+ gc_pin(objspace, src);
+ free_page->free_slots--;
return true;
}
@@ -5823,18 +5855,13 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots;
size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
- /* Some size pools may have very few pages (or even no pages). These size pools
- * should still have allocatable pages. */
- if (min_free_slots < gc_params.heap_init_slots) {
- min_free_slots = gc_params.heap_init_slots;
- }
/* If we don't have enough slots and we have pages on the tomb heap, move
* pages from the tomb heap to the eden heap. This may prevent page
* creation thrashing (frequently allocating and deallocting pages) and
* GC thrashing (running GC more frequently than required). */
struct heap_page *resurrected_page;
- while (swept_slots < min_free_slots &&
+ while ((swept_slots < min_free_slots || swept_slots < gc_params.heap_init_slots) &&
(resurrected_page = heap_page_resurrect(objspace, size_pool))) {
swept_slots += resurrected_page->free_slots;
@@ -5842,6 +5869,17 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
heap_add_freepage(heap, resurrected_page);
}
+ /* Some size pools may have very few pages (or even no pages). These size pools
+ * should still have allocatable pages. */
+ if (min_free_slots < gc_params.heap_init_slots && swept_slots < gc_params.heap_init_slots) {
+ int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
+ size_t extra_slots = gc_params.heap_init_slots - swept_slots;
+ size_t extend_page_count = CEILDIV(extra_slots * multiple, HEAP_PAGE_OBJ_LIMIT);
+ if (extend_page_count > size_pool->allocatable_pages) {
+ size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count);
+ }
+ }
+
if (swept_slots < min_free_slots) {
bool grow_heap = is_full_marking(objspace);
@@ -6069,9 +6107,19 @@ invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_
object = rb_gc_location(forwarding_object);
+ shape_id_t original_shape_id = 0;
+ if (RB_TYPE_P(object, T_OBJECT)) {
+ original_shape_id = RMOVED(forwarding_object)->original_shape_id;
+ }
+
gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size);
/* forwarding_object is now our actual object, and "object"
* is the free slot for the original page */
+
+ if (original_shape_id) {
+ ROBJECT_SET_SHAPE_ID(forwarding_object, original_shape_id);
+ }
+
struct heap_page *orig_page = GET_HEAP_PAGE(object);
orig_page->free_slots++;
heap_page_add_freeobj(objspace, orig_page, object);
@@ -6774,8 +6822,10 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
static void
mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec)
{
- rb_wasm_scan_stack(rb_mark_locations);
- each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe);
+ VALUE *stack_start, *stack_end;
+ SET_STACK_END;
+ GET_STACK_BOUNDS(stack_start, stack_end, 1);
+ each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_maybe);
rb_wasm_scan_locals(rb_mark_locations);
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe);
@@ -7169,6 +7219,8 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
}
}
+static void mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass);
+
static void
gc_mark_children(rb_objspace_t *objspace, VALUE obj)
{
@@ -7215,8 +7267,11 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
if (!RCLASS_EXT(obj)) break;
mark_m_tbl(objspace, RCLASS_M_TBL(obj));
+ mark_cvc_tbl(objspace, obj);
cc_table_mark(objspace, obj);
- mark_tbl_no_pin(objspace, RCLASS_IV_TBL(obj));
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
+ }
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
break;
@@ -7280,16 +7335,31 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_OBJECT:
{
- const VALUE * const ptr = ROBJECT_IVPTR(obj);
+ rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj));
+ if (rb_shape_obj_too_complex(obj)) {
+ mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj));
+ }
+ else {
+ const VALUE * const ptr = ROBJECT_IVPTR(obj);
- uint32_t i, len = ROBJECT_NUMIV(obj);
- for (i = 0; i < len; i++) {
- gc_mark(objspace, ptr[i]);
+ uint32_t i, len = ROBJECT_IV_COUNT(obj);
+ for (i = 0; i < len; i++) {
+ gc_mark(objspace, ptr[i]);
+ }
+
+ if (LIKELY(during_gc) &&
+ ROBJ_TRANSIENT_P(obj)) {
+ rb_transient_heap_mark(obj, ptr);
+ }
}
+ if (shape) {
+ VALUE klass = RBASIC_CLASS(obj);
- if (LIKELY(during_gc) &&
- ROBJ_TRANSIENT_P(obj)) {
- rb_transient_heap_mark(obj, ptr);
+ // Increment max_iv_count if applicable, used to determine size pool allocation
+ uint32_t num_of_ivs = shape->next_iv_index;
+ if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) {
+ RCLASS_EXT(klass)->max_iv_count = num_of_ivs;
+ }
}
}
break;
@@ -7303,6 +7373,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
gc_mark(objspace, any->as.file.fptr->writeconv_pre_ecopts);
gc_mark(objspace, any->as.file.fptr->encs.ecopts);
gc_mark(objspace, any->as.file.fptr->write_lock);
+ gc_mark(objspace, any->as.file.fptr->timeout);
}
break;
@@ -7372,7 +7443,7 @@ gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count)
#endif
while (pop_mark_stack(mstack, &obj)) {
- if (obj == Qundef) continue; /* skip */
+ if (UNDEF_P(obj)) continue; /* skip */
if (RGENGC_CHECK_MODE && !RVALUE_MARKED(obj)) {
rb_bug("gc_mark_stacked_objects: %s is not marked.", obj_info(obj));
@@ -8389,42 +8460,64 @@ static rb_size_pool_t *
gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, VALUE src)
{
size_t obj_size;
+ size_t idx = 0;
switch (BUILTIN_TYPE(src)) {
- case T_ARRAY:
- obj_size = rb_ary_size_as_embedded(src);
- break;
+ case T_ARRAY:
+ obj_size = rb_ary_size_as_embedded(src);
+ break;
- case T_OBJECT:
- obj_size = rb_obj_embedded_size(ROBJECT_NUMIV(src));
- break;
+ case T_OBJECT:
+ if (rb_shape_obj_too_complex(src)) {
+ return &size_pools[0];
+ }
+ else {
+ obj_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(src));
+ }
+ break;
- case T_STRING:
- obj_size = rb_str_size_as_embedded(src);
- break;
+ case T_STRING:
+ obj_size = rb_str_size_as_embedded(src);
+ break;
- default:
- return src_pool;
+ default:
+ return src_pool;
}
if (rb_gc_size_allocatable_p(obj_size)){
- return &size_pools[size_pool_idx_for_size(obj_size)];
- }
- else {
- return &size_pools[0];
+ idx = size_pool_idx_for_size(obj_size);
}
+ return &size_pools[idx];
}
static bool
gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src)
{
GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED);
- rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(gc_compact_destination_pool(objspace, size_pool, src));
+ GC_ASSERT(gc_is_moveable_obj(objspace, src));
+
+ rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, size_pool, src);
+ rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(dest_pool);
+ rb_shape_t *new_shape = NULL;
+ rb_shape_t *orig_shape = NULL;
if (gc_compact_heap_cursors_met_p(dheap)) {
return dheap != heap;
}
+ if (RB_TYPE_P(src, T_OBJECT)) {
+ orig_shape = rb_shape_get_shape(src);
+ if (dheap != heap && !rb_shape_obj_too_complex(src)) {
+ rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + SIZE_POOL_COUNT));
+ new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape);
+
+ if (!new_shape) {
+ dest_pool = size_pool;
+ dheap = heap;
+ }
+ }
+ }
+
while (!try_move(objspace, dheap, dheap->free_pages, src)) {
struct gc_sweep_context ctx = {
.page = dheap->sweeping_page,
@@ -8446,9 +8539,18 @@ gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_p
dheap->sweeping_page = ccan_list_next(&dheap->pages, dheap->sweeping_page, page_node);
if (gc_compact_heap_cursors_met_p(dheap)) {
- return false;
+ return dheap != heap;
}
}
+
+ if (orig_shape) {
+ if (new_shape) {
+ VALUE dest = rb_gc_location(src);
+ rb_shape_set_shape(dest, new_shape);
+ }
+ RMOVED(src)->original_shape_id = rb_shape_id(orig_shape);
+ }
+
return true;
}
@@ -8466,9 +8568,11 @@ gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *
if (bitset & 1) {
objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++;
- if (!gc_compact_move(objspace, heap, size_pool, vp)) {
- //the cursors met. bubble up
- return false;
+ if (gc_is_moveable_obj(objspace, vp)) {
+ if (!gc_compact_move(objspace, heap, size_pool, vp)) {
+ //the cursors met. bubble up
+ return false;
+ }
}
}
p += slot_size;
@@ -8922,8 +9026,10 @@ rb_gc_writebarrier(VALUE a, VALUE b)
{
rb_objspace_t *objspace = &rb_objspace;
- if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const");
- if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const");
+ if (RGENGC_CHECK_MODE) {
+ if (SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const: %"PRIxVALUE, a);
+ if (SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const: %"PRIxVALUE, b);
+ }
retry:
if (!is_incremental_marking(objspace)) {
@@ -9175,10 +9281,23 @@ rb_gc_register_address(VALUE *addr)
rb_objspace_t *objspace = &rb_objspace;
struct gc_list *tmp;
+ VALUE obj = *addr;
+
tmp = ALLOC(struct gc_list);
tmp->next = global_list;
tmp->varptr = addr;
global_list = tmp;
+
+ /*
+ * Because some C extensions have assignment-then-register bugs,
+ * we guard `obj` here so that it would not get swept defensively.
+ */
+ RB_GC_GUARD(obj);
+ if (0 && !SPECIAL_CONST_P(obj)) {
+ rb_warn("Object is assigned to registering address already: %"PRIsVALUE,
+ rb_obj_class(obj));
+ rb_print_backtrace();
+ }
}
void
@@ -9816,6 +9935,17 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
return FALSE;
}
+/* Used in places that could malloc, which can cause the GC to run. We need to
+ * temporarily disable the GC to allow the malloc to happen. */
+#define COULD_MALLOC_REGION_START() \
+ GC_ASSERT(during_gc); \
+ VALUE _already_disabled = rb_gc_disable_no_rest(); \
+ during_gc = false;
+
+#define COULD_MALLOC_REGION_END() \
+ during_gc = true; \
+ if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+
static VALUE
gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size)
{
@@ -9844,11 +9974,12 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS((VALUE)src), (VALUE)src);
if (FL_TEST((VALUE)src, FL_EXIVAR)) {
- /* Same deal as below. Generic ivars are held in st tables.
- * Resizing the table could cause a GC to happen and we can't allow it */
- VALUE already_disabled = rb_gc_disable_no_rest();
- rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
- if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+ /* Resizing the st table could cause a malloc */
+ COULD_MALLOC_REGION_START();
+ {
+ rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
+ }
+ COULD_MALLOC_REGION_END();
}
st_data_t srcid = (st_data_t)src, id;
@@ -9857,17 +9988,25 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
* the object to object id mapping. */
if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) {
gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest);
- /* inserting in the st table can cause the GC to run. We need to
- * prevent re-entry in to the GC since `gc_move` is running in the GC,
- * so temporarily disable the GC around the st table mutation */
- VALUE already_disabled = rb_gc_disable_no_rest();
- st_delete(objspace->obj_to_id_tbl, &srcid, 0);
- st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
- if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
+ /* Resizing the st table could cause a malloc */
+ COULD_MALLOC_REGION_START();
+ {
+ st_delete(objspace->obj_to_id_tbl, &srcid, 0);
+ st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
+ }
+ COULD_MALLOC_REGION_END();
}
/* Move the object */
memcpy(dest, src, MIN(src_slot_size, slot_size));
+
+ if (RVALUE_OVERHEAD > 0) {
+ void *dest_overhead = (void *)(((uintptr_t)dest) + slot_size - RVALUE_OVERHEAD);
+ void *src_overhead = (void *)(((uintptr_t)src) + src_slot_size - RVALUE_OVERHEAD);
+
+ memcpy(dest_overhead, src_overhead, RVALUE_OVERHEAD);
+ }
+
memset(src, 0, src_slot_size);
/* Set bits for object in new location */
@@ -9990,7 +10129,7 @@ gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
}
#if USE_RVARGC
- if ((size_t)GET_HEAP_PAGE(v)->slot_size >= rb_ary_size_as_embedded(v)) {
+ if (rb_gc_obj_slot_size(v) >= rb_ary_size_as_embedded(v)) {
if (rb_ary_embeddable_p(v)) {
rb_ary_make_embedded(v);
}
@@ -10003,14 +10142,18 @@ static void
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
{
VALUE *ptr = ROBJECT_IVPTR(v);
- uint32_t numiv = ROBJECT_NUMIV(v);
+
+ if (rb_shape_obj_too_complex(v)) {
+ rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v));
+ return;
+ }
#if USE_RVARGC
size_t slot_size = rb_gc_obj_slot_size(v);
- size_t embed_size = rb_obj_embedded_size(numiv);
+ size_t embed_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(v));
if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) {
// Object can be re-embedded
- memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * numiv);
+ memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v));
RB_FL_SET_RAW(v, ROBJECT_EMBED);
if (ROBJ_TRANSIENT_P(v)) {
ROBJ_TRANSIENT_UNSET(v);
@@ -10019,18 +10162,10 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
xfree(ptr);
}
ptr = ROBJECT(v)->as.ary;
-
- uint32_t capa = (uint32_t)((slot_size - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
- ROBJECT(v)->numiv = capa;
-
- // Fill end with Qundef
- for (uint32_t i = numiv; i < capa; i++) {
- ptr[i] = Qundef;
- }
}
#endif
- for (uint32_t i = 0; i < numiv; i++) {
+ for (uint32_t i = 0; i < ROBJECT_IV_COUNT(v); i++) {
UPDATE_IF_MOVED(objspace, ptr[i]);
}
}
@@ -10357,9 +10492,14 @@ static enum rb_id_table_iterator_result
update_cvc_tbl_i(VALUE cvc_entry, void *data)
{
struct rb_cvar_class_tbl_entry *entry;
+ rb_objspace_t * objspace = (rb_objspace_t *)data;
entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
+ if (entry->cref) {
+ TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, entry->cref);
+ }
+
entry->class_value = rb_gc_location(entry->class_value);
return ID_TABLE_CONTINUE;
@@ -10375,6 +10515,28 @@ update_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
}
static enum rb_id_table_iterator_result
+mark_cvc_tbl_i(VALUE cvc_entry, void *data)
+{
+ struct rb_cvar_class_tbl_entry *entry;
+
+ entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
+
+ RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref)));
+ rb_gc_mark((VALUE) entry->cref);
+
+ return ID_TABLE_CONTINUE;
+}
+
+static void
+mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
+{
+ struct rb_id_table *tbl = RCLASS_CVC_TBL(klass);
+ if (tbl) {
+ rb_id_table_foreach_values(tbl, mark_cvc_tbl_i, objspace);
+ }
+}
+
+static enum rb_id_table_iterator_result
update_const_table(VALUE value, void *data)
{
rb_const_entry_t *ce = (rb_const_entry_t *)value;
@@ -10407,15 +10569,6 @@ update_subclass_entries(rb_objspace_t *objspace, rb_subclass_entry_t *entry)
}
}
-static int
-update_iv_index_tbl_i(st_data_t key, st_data_t value, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
- struct rb_iv_index_tbl_entry *ent = (struct rb_iv_index_tbl_entry *)value;
- UPDATE_IF_MOVED(objspace, ent->class_value);
- return ST_CONTINUE;
-}
-
static void
update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext)
{
@@ -10423,11 +10576,6 @@ update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext)
UPDATE_IF_MOVED(objspace, ext->includer);
UPDATE_IF_MOVED(objspace, ext->refined_class);
update_subclass_entries(objspace, ext->subclasses);
-
- // ext->iv_index_tbl
- if (ext->iv_index_tbl) {
- st_foreach(ext->iv_index_tbl, update_iv_index_tbl_i, (st_data_t)objspace);
- }
}
static void
@@ -10459,7 +10607,9 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
update_cvc_tbl(objspace, obj);
update_superclasses(objspace, obj);
- gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj));
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]);
+ }
update_class_ext(objspace, RCLASS_EXT(obj));
update_const_tbl(objspace, RCLASS_CONST_TBL(obj));
@@ -10474,9 +10624,6 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
UPDATE_IF_MOVED(objspace, RCLASS(obj)->super);
}
if (!RCLASS_EXT(obj)) break;
- if (RCLASS_IV_TBL(obj)) {
- gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj));
- }
update_class_ext(objspace, RCLASS_EXT(obj));
update_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
update_cc_tbl(objspace, obj);
@@ -10516,16 +10663,18 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
#if USE_RVARGC
VALUE new_root = any->as.string.as.heap.aux.shared;
rb_str_update_shared_ary(obj, old_root, new_root);
+#endif
+ }
- // if, after move the string is not embedded, and can fit in the
- // slot it's been placed in, then re-embed it
- if ((size_t)GET_HEAP_PAGE(obj)->slot_size >= rb_str_size_as_embedded(obj)) {
- if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
- rb_str_make_embedded(obj);
- }
+#if USE_RVARGC
+ /* If, after move the string is not embedded, and can fit in the
+ * slot it's been placed in, then re-embed it. */
+ if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) {
+ if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
+ rb_str_make_embedded(obj);
}
-#endif
}
+#endif
break;
}
@@ -10701,9 +10850,9 @@ gc_update_references(rb_objspace_t *objspace)
#if GC_CAN_COMPILE_COMPACTION
/*
* call-seq:
- * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}}
+ * GC.latest_compact_info -> hash
*
- * Returns information about object moved in the most recent GC compaction.
+ * Returns information about object moved in the most recent \GC compaction.
*
* The returned hash has two keys :considered and :moved. The hash for
* :considered lists the number of objects that were considered for movement
@@ -10807,13 +10956,13 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data)
* This function compacts objects together in Ruby's heap. It eliminates
* unused space (or fragmentation) in the heap by moving objects in to that
* unused space. This function returns a hash which contains statistics about
- * which objects were moved. See `GC.latest_gc_info` for details about
+ * which objects were moved. See <tt>GC.latest_gc_info</tt> for details about
* compaction statistics.
*
* This method is implementation specific and not expected to be implemented
* in any implementation besides MRI.
*
- * To test whether GC compaction is supported, use the idiom:
+ * To test whether \GC compaction is supported, use the idiom:
*
* GC.respond_to?(:compact)
*/
@@ -10934,7 +11083,7 @@ gc_count(rb_execution_context_t *ec, VALUE self)
static VALUE
gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned int orig_flags)
{
- static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state;
+ static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state, sym_need_major_by;
static VALUE sym_nofree, sym_oldgen, sym_shady, sym_force, sym_stress;
#if RGENGC_ESTIMATE_OLDMALLOC
static VALUE sym_oldmalloc;
@@ -10942,7 +11091,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
static VALUE sym_newobj, sym_malloc, sym_method, sym_capi;
static VALUE sym_none, sym_marking, sym_sweeping;
VALUE hash = Qnil, key = Qnil;
- VALUE major_by;
+ VALUE major_by, need_major_by;
unsigned int flags = orig_flags ? orig_flags : objspace->profile.latest_gc_info;
if (SYMBOL_P(hash_or_key)) {
@@ -10962,6 +11111,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
S(immediate_sweep);
S(have_finalizer);
S(state);
+ S(need_major_by);
S(stress);
S(nofree);
@@ -10999,6 +11149,20 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
Qnil;
SET(major_by, major_by);
+ if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */
+ unsigned int need_major_flags = objspace->rgengc.need_major_gc;
+ need_major_by =
+ (need_major_flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady :
+ (need_major_flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force :
+#if RGENGC_ESTIMATE_OLDMALLOC
+ (need_major_flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc :
+#endif
+ Qnil;
+ SET(need_major_by, need_major_by);
+ }
+
SET(gc_by,
(flags & GPR_FLAG_NEWOBJ) ? sym_newobj :
(flags & GPR_FLAG_MALLOC) ? sym_malloc :
@@ -11609,24 +11773,25 @@ get_envparam_double(const char *name, double *default_value, double lower_bound,
}
static void
-gc_set_initial_pages(void)
+gc_set_initial_pages(rb_objspace_t *objspace)
{
- size_t min_pages;
- rb_objspace_t *objspace = &rb_objspace;
-
gc_rest(objspace);
- min_pages = gc_params.heap_init_slots / HEAP_PAGE_OBJ_LIMIT;
-
- size_t pages_per_class = (min_pages - heap_eden_total_pages(objspace)) / SIZE_POOL_COUNT;
-
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
- heap_add_pages(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool), pages_per_class);
+ if (gc_params.heap_init_slots > size_pool->eden_heap.total_slots) {
+ size_t slots = gc_params.heap_init_slots - size_pool->eden_heap.total_slots;
+ int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
+ size_pool->allocatable_pages = slots * multiple / HEAP_PAGE_OBJ_LIMIT;
+ }
+ else {
+ /* We already have more slots than heap_init_slots allows, so
+ * prevent creating more pages. */
+ size_pool->allocatable_pages = 0;
+ }
}
-
- heap_add_pages(objspace, &size_pools[0], SIZE_POOL_EDEN_HEAP(&size_pools[0]), min_pages - heap_eden_total_pages(objspace));
+ heap_pages_expand_sorted(objspace);
}
/*
@@ -11682,7 +11847,7 @@ ruby_gc_set_params(void)
/* RUBY_GC_HEAP_INIT_SLOTS */
if (get_envparam_size("RUBY_GC_HEAP_INIT_SLOTS", &gc_params.heap_init_slots, 0)) {
- gc_set_initial_pages();
+ gc_set_initial_pages(objspace);
}
get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE);
@@ -12098,6 +12263,16 @@ objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
return size;
}
+static bool
+malloc_during_gc_p(rb_objspace_t *objspace)
+{
+ /* malloc is not allowed during GC when we're not using multiple ractors
+ * (since ractors can run while another thread is sweeping) and when we
+ * have the GVL (since if we don't have the GVL, we'll try to acquire the
+ * GVL which will block and ensure the other thread finishes GC). */
+ return during_gc && !rb_multi_ractor_p() && ruby_thread_has_gvl_p();
+}
+
static inline void *
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
{
@@ -12121,10 +12296,16 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
}
#if defined(__GNUC__) && RUBY_DEBUG
-#define RB_BUG_INSTEAD_OF_RB_MEMERROR
+#define RB_BUG_INSTEAD_OF_RB_MEMERROR 1
+#endif
+
+#ifndef RB_BUG_INSTEAD_OF_RB_MEMERROR
+# define RB_BUG_INSTEAD_OF_RB_MEMERROR 0
#endif
-#ifdef RB_BUG_INSTEAD_OF_RB_MEMERROR
+#define GC_MEMERROR(...) \
+ ((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : rb_memerror())
+
#define TRY_WITH_GC(siz, expr) do { \
const gc_profile_record_flag gpr = \
GPR_FLAG_FULL_MARK | \
@@ -12138,29 +12319,27 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
} \
else if (!garbage_collect_with_gvl(objspace, gpr)) { \
/* @shyouhei thinks this doesn't happen */ \
- rb_bug("TRY_WITH_GC: could not GC"); \
+ GC_MEMERROR("TRY_WITH_GC: could not GC"); \
} \
else if ((expr)) { \
/* Success on 2nd try */ \
} \
else { \
- rb_bug("TRY_WITH_GC: could not allocate:" \
- "%"PRIdSIZE" bytes for %s", \
- siz, # expr); \
+ GC_MEMERROR("TRY_WITH_GC: could not allocate:" \
+ "%"PRIdSIZE" bytes for %s", \
+ siz, # expr); \
} \
} while (0)
-#else
-#define TRY_WITH_GC(siz, alloc) do { \
- objspace_malloc_gc_stress(objspace); \
- if (!(alloc) && \
- (!garbage_collect_with_gvl(objspace, GPR_FLAG_FULL_MARK | \
- GPR_FLAG_IMMEDIATE_MARK | GPR_FLAG_IMMEDIATE_SWEEP | \
- GPR_FLAG_MALLOC) || \
- !(alloc))) { \
- ruby_memerror(); \
- } \
- } while (0)
-#endif
+
+static void
+check_malloc_not_in_gc(rb_objspace_t *objspace, const char *msg)
+{
+ if (UNLIKELY(malloc_during_gc_p(objspace))) {
+ dont_gc_on();
+ during_gc = false;
+ rb_bug("Cannot %s during GC", msg);
+ }
+}
/* these shouldn't be called directly.
* objspace_* functions do not check allocation size.
@@ -12168,6 +12347,8 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
static void *
objspace_xmalloc0(rb_objspace_t *objspace, size_t size)
{
+ check_malloc_not_in_gc(objspace, "malloc");
+
void *mem;
size = objspace_malloc_prepare(objspace, size);
@@ -12185,6 +12366,8 @@ xmalloc2_size(const size_t count, const size_t elsize)
static void *
objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size)
{
+ check_malloc_not_in_gc(objspace, "realloc");
+
void *mem;
if (!ptr) return objspace_xmalloc0(objspace, new_size);
@@ -12242,7 +12425,7 @@ objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t ol
#endif
old_size = objspace_malloc_size(objspace, ptr, old_size);
- TRY_WITH_GC(new_size, mem = realloc(ptr, new_size));
+ TRY_WITH_GC(new_size, mem = RB_GNUC_EXTENSION_BLOCK(realloc(ptr, new_size)));
new_size = objspace_malloc_size(objspace, mem, new_size);
#if CALC_EXACT_MALLOC_SIZE
@@ -12422,6 +12605,13 @@ ruby_xmalloc2_body(size_t n, size_t size)
static void *
objspace_xcalloc(rb_objspace_t *objspace, size_t size)
{
+ if (UNLIKELY(malloc_during_gc_p(objspace))) {
+ rb_warn("calloc during GC detected, this could cause crashes if it triggers another GC");
+#if RGENGC_CHECK_MODE || RUBY_DEBUG
+ rb_bug("Cannot calloc during GC");
+#endif
+ }
+
void *mem;
size = objspace_malloc_prepare(objspace, size);
@@ -12476,8 +12666,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
void
ruby_sized_xfree(void *x, size_t size)
{
- if (x) {
- objspace_xfree(&rb_objspace, x, size);
+ if (LIKELY(x)) {
+ /* It's possible for a C extension's pthread destructor function set by pthread_key_create
+ * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
+ * that case. */
+ if (LIKELY(GET_VM())) {
+ objspace_xfree(&rb_objspace, x, size);
+ }
+ else {
+ ruby_mimfree(x);
+ }
}
}
@@ -12671,12 +12869,47 @@ wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
}
#endif
+static int
+wmap_replace_ref(st_data_t *key, st_data_t *value, st_data_t _argp, int existing)
+{
+ *key = rb_gc_location((VALUE)*key);
+
+ VALUE *values = (VALUE *)*value;
+ VALUE size = values[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ values[index] = rb_gc_location(values[index]);
+ }
+
+ return ST_CONTINUE;
+}
+
+static int
+wmap_foreach_replace(st_data_t key, st_data_t value, st_data_t _argp, int error)
+{
+ if (rb_gc_location((VALUE)key) != (VALUE)key) {
+ return ST_REPLACE;
+ }
+
+ VALUE *values = (VALUE *)value;
+ VALUE size = values[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ VALUE val = values[index];
+ if (rb_gc_location(val) != val) {
+ return ST_REPLACE;
+ }
+ }
+
+ return ST_CONTINUE;
+}
+
static void
wmap_compact(void *ptr)
{
struct weakmap *w = ptr;
if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj);
- if (w->obj2wmap) rb_gc_update_tbl_refs(w->obj2wmap);
+ if (w->obj2wmap) st_foreach_with_replace(w->obj2wmap, wmap_foreach_replace, wmap_replace_ref, (st_data_t)NULL);
w->final = rb_gc_location(w->final);
}
@@ -12705,6 +12938,7 @@ wmap_free(void *ptr)
st_foreach(w->obj2wmap, wmap_free_map, 0);
st_free_table(w->obj2wmap);
st_free_table(w->wmap2obj);
+ xfree(w);
}
static int
@@ -12773,25 +13007,40 @@ wmap_live_p(rb_objspace_t *objspace, VALUE obj)
}
static int
-wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+wmap_remove_inverse_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
{
- VALUE wmap, *ptr, size, i, j;
if (!existing) return ST_STOP;
- wmap = (VALUE)arg, ptr = (VALUE *)*value;
- for (i = j = 1, size = ptr[0]; i <= size; ++i) {
- if (ptr[i] != wmap) {
- ptr[j++] = ptr[i];
- }
- }
- if (j == 1) {
- ruby_sized_xfree(ptr, i * sizeof(VALUE));
+
+ VALUE old_ref = (VALUE)arg;
+
+ VALUE *values = (VALUE *)*val;
+ VALUE size = values[0];
+
+ if (size == 1) {
+ // fast path, we only had one backref
+ RUBY_ASSERT(values[1] == old_ref);
+ ruby_sized_xfree(values, 2 * sizeof(VALUE));
return ST_DELETE;
}
- if (j < i) {
- SIZED_REALLOC_N(ptr, VALUE, j + 1, i);
- ptr[0] = j;
- *value = (st_data_t)ptr;
+
+ bool found = false;
+ VALUE index = 1;
+ for (; index <= size; index++) {
+ if (values[index] == old_ref) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) return ST_STOP;
+
+ if (size > index) {
+ MEMMOVE(&values[index], &values[index + 1], VALUE, size - index);
}
+
+ size -= 1;
+ values[0] = size;
+ SIZED_REALLOC_N(values, VALUE, size + 1, size + 2);
+ *val = (st_data_t)values;
return ST_CONTINUE;
}
@@ -12805,7 +13054,7 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
/* Get reference from object id. */
- if ((obj = id2ref_obj_tbl(&rb_objspace, objid)) == Qundef) {
+ if (UNDEF_P(obj = id2ref_obj_tbl(&rb_objspace, objid))) {
rb_bug("wmap_finalize: objid is not found.");
}
@@ -12824,7 +13073,7 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
wmap = (st_data_t)obj;
if (st_delete(w->wmap2obj, &wmap, &orig)) {
wmap = (st_data_t)obj;
- st_update(w->obj2wmap, orig, wmap_final_func, wmap);
+ st_update(w->obj2wmap, orig, wmap_remove_inverse_ref, wmap);
}
return self;
}
@@ -13040,6 +13289,14 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
VALUE size, *ptr, *optr;
if (existing) {
size = (ptr = optr = (VALUE *)*val)[0];
+
+ for (VALUE index = 1; index <= size; index++) {
+ if (ptr[index] == (VALUE)arg) {
+ // The reference was already registered.
+ return ST_STOP;
+ }
+ }
+
++size;
SIZED_REALLOC_N(ptr, VALUE, size + 1, size);
}
@@ -13055,6 +13312,23 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
return ST_CONTINUE;
}
+struct wmap_aset_replace_args {
+ VALUE new_value;
+ VALUE old_value;
+};
+
+static int
+wmap_aset_replace_value(st_data_t *key, st_data_t *val, st_data_t _args, int existing)
+{
+ struct wmap_aset_replace_args *args = (struct wmap_aset_replace_args *)_args;
+
+ if (existing) {
+ args->old_value = *val;
+ }
+ *val = (st_data_t)args->new_value;
+ return ST_CONTINUE;
+}
+
/* Creates a weak reference from the given key to the given value */
static VALUE
wmap_aset(VALUE self, VALUE key, VALUE value)
@@ -13069,8 +13343,25 @@ wmap_aset(VALUE self, VALUE key, VALUE value)
define_final0(key, w->final);
}
- st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
- st_insert(w->wmap2obj, (st_data_t)key, (st_data_t)value);
+ struct wmap_aset_replace_args aset_args = {
+ .new_value = value,
+ .old_value = Qundef,
+ };
+ st_update(w->wmap2obj, (st_data_t)key, wmap_aset_replace_value, (st_data_t)&aset_args);
+
+ // If the value is unchanged, we have nothing to do.
+ if (value != aset_args.old_value) {
+ if (!UNDEF_P(aset_args.old_value) && FL_ABLE(aset_args.old_value)) {
+ // That key existed and had an inverse reference, we need to clear the outdated inverse reference.
+ st_update(w->obj2wmap, (st_data_t)aset_args.old_value, wmap_remove_inverse_ref, key);
+ }
+
+ if (FL_ABLE(value)) {
+ // If the value has no finalizer, we don't need to keep the inverse reference
+ st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
+ }
+ }
+
return nonspecial_obj_id(value);
}
@@ -13096,14 +13387,14 @@ static VALUE
wmap_aref(VALUE self, VALUE key)
{
VALUE obj = wmap_lookup(self, key);
- return obj != Qundef ? obj : Qnil;
+ return !UNDEF_P(obj) ? obj : Qnil;
}
/* Returns +true+ if +key+ is registered */
static VALUE
wmap_has_key(VALUE self, VALUE key)
{
- return RBOOL(wmap_lookup(self, key) != Qundef);
+ return RBOOL(!UNDEF_P(wmap_lookup(self, key)));
}
/* Returns the number of referenced objects */
@@ -13370,7 +13661,7 @@ gc_prof_set_heap_info(rb_objspace_t *objspace)
* call-seq:
* GC::Profiler.clear -> nil
*
- * Clears the GC profiler data.
+ * Clears the \GC profiler data.
*
*/
@@ -13684,7 +13975,7 @@ gc_profile_total_time(VALUE self)
* call-seq:
* GC::Profiler.enabled? -> true or false
*
- * The current status of GC profile mode.
+ * The current status of \GC profile mode.
*/
static VALUE
@@ -13698,7 +13989,7 @@ gc_profile_enable_get(VALUE self)
* call-seq:
* GC::Profiler.enable -> nil
*
- * Starts the GC profiler.
+ * Starts the \GC profiler.
*
*/
@@ -13715,7 +14006,7 @@ gc_profile_enable(VALUE _)
* call-seq:
* GC::Profiler.disable -> nil
*
- * Stops the GC profiler.
+ * Stops the \GC profiler.
*
*/
@@ -13804,11 +14095,10 @@ rb_raw_iseq_info(char *const buff, const size_t buff_size, const rb_iseq_t *iseq
{
if (buff_size > 0 && ISEQ_BODY(iseq) && ISEQ_BODY(iseq)->location.label && !RB_TYPE_P(ISEQ_BODY(iseq)->location.pathobj, T_MOVED)) {
VALUE path = rb_iseq_path(iseq);
- VALUE n = ISEQ_BODY(iseq)->location.first_lineno;
+ int n = ISEQ_BODY(iseq)->location.first_lineno;
snprintf(buff, buff_size, " %s@%s:%d",
RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(path),
- n ? FIX2INT(n) : 0 );
+ RSTRING_PTR(path), n);
}
}
@@ -13979,7 +14269,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
}
case T_OBJECT:
{
- uint32_t len = ROBJECT_NUMIV(obj);
+ uint32_t len = ROBJECT_IV_CAPACITY(obj);
if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
APPEND_F("(embed) len:%d", len);
@@ -14021,7 +14311,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
{
const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment;
- APPEND_F(":%s (%s%s%s%s) type:%s alias:%d owner:%p defined_class:%p",
+ APPEND_F(":%s (%s%s%s%s) type:%s aliased:%d owner:%p defined_class:%p",
rb_id2name(me->called_id),
METHOD_ENTRY_VISI(me) == METHOD_VISI_PUBLIC ? "pub" :
METHOD_ENTRY_VISI(me) == METHOD_VISI_PRIVATE ? "pri" : "pro",
@@ -14029,7 +14319,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
METHOD_ENTRY_CACHED(me) ? ",cc" : "",
METHOD_ENTRY_INVALIDATED(me) ? ",inv" : "",
me->def ? rb_method_type_name(me->def->type) : "NULL",
- me->def ? me->def->alias_count : -1,
+ me->def ? me->def->aliased : -1,
(void *)me->owner, // obj_info(me->owner),
(void *)me->defined_class); //obj_info(me->defined_class)));
@@ -14320,6 +14610,22 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
*/
#include "gc.rbinc"
+/*
+ * call-seq:
+ * GC.using_rvargc? -> true or false
+ *
+ * Returns true if using experimental feature Variable Width Allocation, false
+ * otherwise.
+ */
+static VALUE
+gc_using_rvargc_p(VALUE mod)
+{
+#if USE_RVARGC
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
void
Init_GC(void)
@@ -14333,7 +14639,8 @@ Init_GC(void)
gc_constants = rb_hash_new();
rb_hash_aset(gc_constants, ID2SYM(rb_intern("DEBUG")), RBOOL(GC_DEBUG));
- rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE));
+ rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE - RVALUE_OVERHEAD));
+ rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), SIZET2NUM(RVALUE_OVERHEAD));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(sizeof(RVALUE)));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_OBJ_LIMIT")), SIZET2NUM(HEAP_PAGE_OBJ_LIMIT));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE));
@@ -14398,6 +14705,8 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
#endif
+ rb_define_singleton_method(rb_mGC, "using_rvargc?", gc_using_rvargc_p, 0);
+
if (GC_COMPACTION_SUPPORTED) {
rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0);
rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0);
@@ -14420,7 +14729,7 @@ Init_GC(void)
{
VALUE opts;
- /* GC build options */
+ /* \GC build options */
rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new());
#define OPT(o) if (o) rb_ary_push(opts, rb_fstring_lit(#o))
OPT(GC_DEBUG);
diff --git a/gc.h b/gc.h
index 3d863fce8a..23218c1a9e 100644
--- a/gc.h
+++ b/gc.h
@@ -6,10 +6,12 @@
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movq\t%%rsp, %0" : "=r" (*(p)))
#elif defined(__i386) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movl\t%%esp, %0" : "=r" (*(p)))
-#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && !defined(_AIX)
+#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && !defined(_AIX) && !defined(__APPLE__) // Not Apple is NEEDED to unbreak ppc64 build on Darwin. Don't ask.
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mr\t%0, %%r1" : "=r" (*(p)))
#elif (defined(__powerpc__) || defined(__powerpc64__)) && defined(__GNUC__) && defined(_AIX)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mr %0,1" : "=r" (*(p)))
+#elif defined(__POWERPC__) && defined(__APPLE__) // Darwin ppc and ppc64
+#define SET_MACHINE_STACK_END(p) __asm__ volatile("mr %0, r1" : "=r" (*(p)))
#elif defined(__aarch64__) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("mov\t%0, sp" : "=r" (*(p)))
#else
@@ -114,10 +116,10 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr);
const char *rb_obj_info(VALUE obj);
const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj);
-VALUE rb_gc_disable_no_rest(void);
-
struct rb_thread_struct;
+size_t rb_size_pool_slot_size(unsigned char pool_id);
+
RUBY_SYMBOL_EXPORT_BEGIN
/* exports for objspace module */
@@ -138,6 +140,8 @@ void rb_objspace_each_objects_without_setup(
size_t rb_gc_obj_slot_size(VALUE obj);
+VALUE rb_gc_disable_no_rest(void);
+
RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_GC_H */
diff --git a/gc.rb b/gc.rb
index 9144a96603..57aeeb9131 100644
--- a/gc.rb
+++ b/gc.rb
@@ -6,7 +6,7 @@
# Some of the underlying methods are also available via the ObjectSpace
# module.
#
-# You may obtain information about the operation of the GC through
+# You may obtain information about the operation of the \GC through
# GC::Profiler.
module GC
@@ -24,7 +24,7 @@ module GC
#
# def GC.start(full_mark: true, immediate_sweep: true); end
#
- # Use full_mark: false to perform a minor GC.
+ # Use full_mark: false to perform a minor \GC.
# Use immediate_sweep: false to defer sweeping (use lazy sweep).
#
# Note: These keyword arguments are implementation and version dependent. They
@@ -67,7 +67,7 @@ module GC
# call-seq:
# GC.stress -> integer, true or false
#
- # Returns current status of GC stress mode.
+ # Returns current status of \GC stress mode.
def self.stress
Primitive.gc_stress_get
end
@@ -75,9 +75,9 @@ module GC
# call-seq:
# GC.stress = flag -> flag
#
- # Updates the GC stress mode.
+ # Updates the \GC stress mode.
#
- # When stress mode is enabled, the GC is invoked at every GC opportunity:
+ # When stress mode is enabled, the \GC is invoked at every \GC opportunity:
# all memory and object allocations.
#
# Enabling stress mode will degrade performance, it is only for debugging.
@@ -93,9 +93,9 @@ module GC
# call-seq:
# GC.count -> Integer
#
- # The number of times GC occurred.
+ # The number of times \GC occurred.
#
- # It returns the number of times GC occurred since the process started.
+ # It returns the number of times \GC occurred since the process started.
def self.count
Primitive.gc_count
end
@@ -105,12 +105,12 @@ module GC
# GC.stat(hash) -> Hash
# GC.stat(:key) -> Numeric
#
- # Returns a Hash containing information about the GC.
+ # Returns a Hash containing information about the \GC.
#
# The contents of the hash are implementation specific and may change in
# the future without notice.
#
- # The hash includes information about internal statistics about GC such as:
+ # The hash includes information about internal statistics about \GC such as:
#
# [count]
# The total number of garbage collections ran since application start
@@ -123,7 +123,7 @@ module GC
# The number of pages that can fit into the buffer that holds references to
# all pages
# [heap_allocatable_pages]
- # The total number of pages the application could allocate without additional GC
+ # The total number of pages the application could allocate without additional \GC
# [heap_available_slots]
# The total number of slots in all `:heap_allocated_pages`
# [heap_live_slots]
@@ -133,7 +133,7 @@ module GC
# [heap_final_slots]
# The total number of slots with pending finalizers to be run
# [heap_marked_slots]
- # The total number of objects marked in the last GC
+ # The total number of objects marked in the last \GC
# [heap_eden_pages]
# The total number of pages which contain at least one live slot
# [heap_tomb_pages]
@@ -147,9 +147,9 @@ module GC
# [total_freed_objects]
# The cumulative number of objects freed since application start
# [malloc_increase_bytes]
- # Amount of memory allocated on the heap for objects. Decreased by any GC
+ # Amount of memory allocated on the heap for objects. Decreased by any \GC
# [malloc_increase_bytes_limit]
- # When `:malloc_increase_bytes` crosses this limit, GC is triggered
+ # When `:malloc_increase_bytes` crosses this limit, \GC is triggered
# [minor_gc_count]
# The total number of minor garbage collections run since process start
# [major_gc_count]
@@ -165,15 +165,15 @@ module GC
# The total number of objects without write barriers
# [remembered_wb_unprotected_objects_limit]
# When `:remembered_wb_unprotected_objects` crosses this limit,
- # major GC is triggered
+ # major \GC is triggered
# [old_objects]
# Number of live, old objects which have survived at least 3 garbage collections
# [old_objects_limit]
- # When `:old_objects` crosses this limit, major GC is triggered
+ # When `:old_objects` crosses this limit, major \GC is triggered
# [oldmalloc_increase_bytes]
- # Amount of memory allocated on the heap for objects. Decreased by major GC
+ # Amount of memory allocated on the heap for objects. Decreased by major \GC
# [oldmalloc_increase_bytes_limit]
- # When `:old_malloc_increase_bytes` crosses this limit, major GC is triggered
+ # When `:old_malloc_increase_bytes` crosses this limit, major \GC is triggered
#
# If the optional argument, hash, is given,
# it is overwritten and returned.
@@ -191,7 +191,7 @@ module GC
# GC.stat_heap(heap_name, hash) -> Hash
# GC.stat_heap(heap_name, :key) -> Numeric
#
- # Returns information for memory pools in the GC.
+ # Returns information for memory pools in the \GC.
#
# If the first optional argument, +heap_name+, is passed in and not +nil+, it
# returns a +Hash+ containing information about the particular memory pool.
@@ -218,12 +218,12 @@ module GC
Primitive.gc_stat_heap heap_name, hash_or_key
end
- # call-seq:
- # GC.latest_gc_info -> {:gc_by=>:newobj}
+ # call-seq:
+ # GC.latest_gc_info -> hash
# GC.latest_gc_info(hash) -> hash
# GC.latest_gc_info(:major_by) -> :malloc
#
- # Returns information about the most recent garbage collection.
+ # Returns information about the most recent garbage collection.
#
# If the optional argument, hash, is given,
# it is overwritten and returned.
@@ -244,7 +244,7 @@ module GC
#
# This function expands the heap to ensure room to move all objects,
# compacts the heap to make sure everything moves, updates all references,
- # then performs a full GC. If any object contains a reference to a T_MOVED
+ # then performs a full \GC. If any object contains a reference to a T_MOVED
# object, that object should be pushed on the mark stack, and will
# make a SEGV.
def self.verify_compaction_references(toward: nil, double_heap: false, expand_heap: false)
@@ -253,21 +253,11 @@ module GC
end
# call-seq:
- # GC.using_rvargc? -> true or false
- #
- # Returns true if using experimental feature Variable Width Allocation, false
- # otherwise.
- def self.using_rvargc? # :nodoc:
- GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] > 1
- end
-
-
- # call-seq:
# GC.measure_total_time = true/false
#
- # Enable to measure GC time.
+ # Enable to measure \GC time.
# You can get the result with <tt>GC.stat(:time)</tt>.
- # Note that GC time measurement can cause some performance overhead.
+ # Note that \GC time measurement can cause some performance overhead.
def self.measure_total_time=(flag)
Primitive.cstmt! %{
rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE;
@@ -289,7 +279,7 @@ module GC
# call-seq:
# GC.total_time -> int
#
- # Return measured GC total time in nano seconds.
+ # Return measured \GC total time in nano seconds.
def self.total_time
Primitive.cexpr! %{
ULL2NUM(rb_objspace.profile.total_time_ns)
diff --git a/gems/bundled_gems b/gems/bundled_gems
index 63536571d9..d37d869d41 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -1,16 +1,16 @@
# gem-name version-to-bundle repository-url [optional-commit-hash-to-test-or-defaults-to-v-version]
-minitest 5.16.3 https://github.com/seattlerb/minitest
-power_assert 2.0.1 https://github.com/ruby/power_assert
+minitest 5.25.1 https://github.com/seattlerb/minitest
+power_assert 2.0.3 https://github.com/ruby/power_assert
rake 13.0.6 https://github.com/ruby/rake
-test-unit 3.5.3 https://github.com/test-unit/test-unit
-rexml 3.2.5 https://github.com/ruby/rexml
-rss 0.2.9 https://github.com/ruby/rss
-net-ftp 0.1.3 https://github.com/ruby/net-ftp
-net-imap 0.2.3 https://github.com/ruby/net-imap
-net-pop 0.1.1 https://github.com/ruby/net-pop
-net-smtp 0.3.1 https://github.com/ruby/net-smtp
+test-unit 3.5.7 https://github.com/test-unit/test-unit
+rexml 3.3.9 https://github.com/ruby/rexml
+rss 0.3.1 https://github.com/ruby/rss
+net-ftp 0.2.1 https://github.com/ruby/net-ftp
+net-imap 0.3.9 https://github.com/ruby/net-imap
+net-pop 0.1.2 https://github.com/ruby/net-pop
+net-smtp 0.3.4 https://github.com/ruby/net-smtp
matrix 0.4.2 https://github.com/ruby/matrix
prime 0.1.2 https://github.com/ruby/prime
-rbs 2.6.0 https://github.com/ruby/rbs 5202d4eeed3257448f19004b4baac4bcf4127717
+rbs 2.8.2 https://github.com/ruby/rbs
typeprof 0.21.3 https://github.com/ruby/typeprof
-debug 1.6.2 https://github.com/ruby/debug e7c37486ff9579251e5d25645b8d38ec96708f12
+debug 1.7.1 https://github.com/ruby/debug
diff --git a/gems/lib/core_assertions.rb b/gems/lib/core_assertions.rb
new file mode 100644
index 0000000000..7334063885
--- /dev/null
+++ b/gems/lib/core_assertions.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/core_assertions.rb"
diff --git a/gems/lib/envutil.rb b/gems/lib/envutil.rb
new file mode 100644
index 0000000000..d684c22cf2
--- /dev/null
+++ b/gems/lib/envutil.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/envutil.rb"
diff --git a/tool/dummy-rake-compiler/rake/extensiontask.rb b/gems/lib/rake/extensiontask.rb
index 62b7ff8018..fdbe8d8874 100644
--- a/tool/dummy-rake-compiler/rake/extensiontask.rb
+++ b/gems/lib/rake/extensiontask.rb
@@ -1,8 +1,11 @@
+require "rake/tasklib" unless defined?(Rake::TaskLib)
+
module Rake
class ExtensionTask < TaskLib
def initialize(...)
- task :compile do
+ task :compile do |args|
puts "Dummy `compile` task defined in #{__FILE__}"
+ puts "#{args.name} => #{args.prereqs.join(' ')}"
end
end
end
diff --git a/hash.c b/hash.c
index 8c20791ede..d2dce30624 100644
--- a/hash.c
+++ b/hash.c
@@ -28,6 +28,7 @@
#include "internal.h"
#include "internal/array.h"
#include "internal/bignum.h"
+#include "internal/basic_operators.h"
#include "internal/class.h"
#include "internal/cont.h"
#include "internal/error.h"
@@ -93,9 +94,11 @@ rb_hash_freeze(VALUE hash)
VALUE rb_cHash;
static VALUE envtbl;
-static ID id_hash, id_default, id_flatten_bang;
+static ID id_hash, id_flatten_bang;
static ID id_hash_iter_lev;
+#define id_default idDefault
+
VALUE
rb_hash_set_ifnone(VALUE hash, VALUE ifnone)
{
@@ -111,7 +114,7 @@ rb_any_cmp(VALUE a, VALUE b)
RB_TYPE_P(b, T_STRING) && RBASIC(b)->klass == rb_cString) {
return rb_str_hash_cmp(a, b);
}
- if (a == Qundef || b == Qundef) return -1;
+ if (UNDEF_P(a) || UNDEF_P(b)) return -1;
if (SYMBOL_P(a) && SYMBOL_P(b)) {
return a != b;
}
@@ -195,7 +198,7 @@ obj_any_hash(VALUE obj)
{
VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0);
- if (hval == Qundef) {
+ if (UNDEF_P(hval)) {
hval = rb_exec_recursive_outer_mid(hash_recursive, obj, 0, id_hash);
}
@@ -218,7 +221,7 @@ obj_any_hash(VALUE obj)
return FIX2LONG(hval);
}
-static st_index_t
+st_index_t
rb_any_hash(VALUE a)
{
return any_hash(a, obj_any_hash);
@@ -358,6 +361,9 @@ const struct st_hash_type rb_hashtype_ident = {
rb_ident_hash,
};
+#define RHASH_IDENTHASH_P(hash) (RHASH_TYPE(hash) == &identhash)
+#define RHASH_STRING_KEY_P(hash, key) (!RHASH_IDENTHASH_P(hash) && (rb_obj_class(key) == rb_cString))
+
typedef st_index_t st_hash_t;
/*
@@ -437,7 +443,7 @@ ar_cleared_entry(VALUE hash, unsigned int index)
* so you need to check key == Qundef
*/
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, index);
- return pair->key == Qundef;
+ return UNDEF_P(pair->key);
}
else {
return FALSE;
@@ -517,8 +523,8 @@ hash_verify_(VALUE hash, const char *file, int line)
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
k = pair->key;
v = pair->val;
- HASH_ASSERT(k != Qundef);
- HASH_ASSERT(v != Qundef);
+ HASH_ASSERT(!UNDEF_P(k));
+ HASH_ASSERT(!UNDEF_P(v));
n++;
}
}
@@ -764,31 +770,39 @@ ar_free_and_clear_table(VALUE hash)
HASH_ASSERT(RHASH_TRANSIENT_P(hash) == 0);
}
-static void
-ar_try_convert_table(VALUE hash)
-{
- if (!RHASH_AR_TABLE_P(hash)) return;
-
- const unsigned size = RHASH_AR_TABLE_SIZE(hash);
+void rb_st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash); // st.c
- st_table *new_tab;
- st_index_t i;
+enum ar_each_key_type {
+ ar_each_key_copy,
+ ar_each_key_cmp,
+ ar_each_key_insert,
+};
- if (size < RHASH_AR_TABLE_MAX_SIZE) {
- return;
+static inline int
+ar_each_key(ar_table *ar, int max, enum ar_each_key_type type, st_data_t *dst_keys, st_table *new_tab, st_hash_t *hashes)
+{
+ for (int i = 0; i < max; i++) {
+ ar_table_pair *pair = &ar->pairs[i];
+
+ switch (type) {
+ case ar_each_key_copy:
+ dst_keys[i] = pair->key;
+ break;
+ case ar_each_key_cmp:
+ if (dst_keys[i] != pair->key) return 1;
+ break;
+ case ar_each_key_insert:
+ if (UNDEF_P(pair->key)) continue; // deleted entry
+ rb_st_add_direct_with_hash(new_tab, pair->key, pair->val, hashes[i]);
+ break;
+ }
}
- new_tab = st_init_table_with_size(&objhash, size * 2);
-
- for (i = 0; i < RHASH_AR_TABLE_MAX_BOUND; i++) {
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
- }
- ar_free_and_clear_table(hash);
- RHASH_ST_TABLE_SET(hash, new_tab);
- return;
+ return 0;
}
+
+
static st_table *
ar_force_convert_table(VALUE hash, const char *file, int line)
{
@@ -799,22 +813,32 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
}
if (RHASH_AR_TABLE(hash)) {
- unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
-
-#if defined(RHASH_CONVERT_TABLE_DEBUG) && RHASH_CONVERT_TABLE_DEBUG
- rb_obj_info_dump(hash);
- fprintf(stderr, "force_convert: %s:%d\n", file, line);
- RB_DEBUG_COUNTER_INC(obj_hash_force_convert);
-#endif
+ ar_table *ar = RHASH_AR_TABLE(hash);
+ st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE];
+ unsigned int bound, size;
+
+ // prepare hash values
+ do {
+ st_data_t keys[RHASH_AR_TABLE_MAX_SIZE];
+ bound = RHASH_AR_TABLE_BOUND(hash);
+ size = RHASH_AR_TABLE_SIZE(hash);
+ ar_each_key(ar, bound, ar_each_key_copy, keys, NULL, NULL);
+
+ for (unsigned int i = 0; i < bound; i++) {
+ // do_hash calls #hash method and it can modify hash object
+ hashes[i] = UNDEF_P(keys[i]) ? 0 : ar_do_hash(keys[i]);
+ }
- new_tab = st_init_table_with_size(&objhash, RHASH_AR_TABLE_SIZE(hash));
+ // check if modified
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) return RHASH_ST_TABLE(hash);
+ if (UNLIKELY(RHASH_AR_TABLE_BOUND(hash) != bound)) continue;
+ if (UNLIKELY(ar_each_key(ar, bound, ar_each_key_cmp, keys, NULL, NULL))) continue;
+ } while (0);
- for (i = 0; i < bound; i++) {
- if (ar_cleared_entry(hash, i)) continue;
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
- }
+ // make st
+ new_tab = st_init_table_with_size(&objhash, size);
+ ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes);
ar_free_and_clear_table(hash);
}
else {
@@ -963,7 +987,7 @@ ar_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg)
static int
ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg,
- st_data_t never)
+ st_data_t never)
{
if (RHASH_AR_TABLE_SIZE(hash) > 0) {
unsigned i, ret = 0, bound = RHASH_AR_TABLE_BOUND(hash);
@@ -984,13 +1008,13 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
switch (retval) {
case ST_CHECK: {
- pair = RHASH_AR_TABLE_REF(hash, i);
- if (pair->key == never) break;
- ret = ar_find_entry_hint(hash, hint, key);
- if (ret == RHASH_AR_TABLE_MAX_BOUND) {
- retval = (*func)(0, 0, arg, 1);
- return 2;
- }
+ pair = RHASH_AR_TABLE_REF(hash, i);
+ if (pair->key == never) break;
+ ret = ar_find_entry_hint(hash, hint, key);
+ if (ret == RHASH_AR_TABLE_MAX_BOUND) {
+ retval = (*func)(0, 0, arg, 1);
+ return 2;
+ }
}
case ST_CONTINUE:
break;
@@ -998,11 +1022,11 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
case ST_REPLACE:
return 0;
case ST_DELETE: {
- if (!ar_cleared_entry(hash, i)) {
- ar_clear_entry(hash, i);
- RHASH_AR_TABLE_SIZE_DEC(hash);
- }
- break;
+ if (!ar_cleared_entry(hash, i)) {
+ ar_clear_entry(hash, i);
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ }
+ break;
}
}
}
@@ -1346,15 +1370,8 @@ struct hash_foreach_arg {
};
static int
-hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
+hash_iter_status_check(int status)
{
- struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
- int status;
-
- if (error) return ST_STOP;
- status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
- /* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
-
switch (status) {
case ST_DELETE:
return ST_DELETE;
@@ -1363,31 +1380,38 @@ hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
case ST_STOP:
return ST_STOP;
}
+
return ST_CHECK;
}
static int
+hash_ar_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
+{
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
+
+ if (error) return ST_STOP;
+
+ int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+ /* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
+
+ return hash_iter_status_check(status);
+}
+
+static int
hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
{
struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
- int status;
- st_table *tbl;
if (error) return ST_STOP;
- tbl = RHASH_ST_TABLE(arg->hash);
- status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+
+ st_table *tbl = RHASH_ST_TABLE(arg->hash);
+ int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
+
if (RHASH_ST_TABLE(arg->hash) != tbl) {
- rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
+ rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
}
- switch (status) {
- case ST_DELETE:
- return ST_DELETE;
- case ST_CONTINUE:
- break;
- case ST_STOP:
- return ST_STOP;
- }
- return ST_CHECK;
+
+ return hash_iter_status_check(status);
}
static int
@@ -1406,13 +1430,19 @@ iter_lev_in_ivar_set(VALUE hash, int lev)
rb_ivar_set_internal(hash, id_hash_iter_lev, INT2FIX(lev));
}
-static int
+static inline int
iter_lev_in_flags(VALUE hash)
{
unsigned int u = (unsigned int)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX);
return (int)u;
}
+static inline void
+iter_lev_in_flags_set(VALUE hash, int lev)
+{
+ RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
+}
+
static int
RHASH_ITER_LEV(VALUE hash)
{
@@ -1436,7 +1466,7 @@ hash_iter_lev_inc(VALUE hash)
}
else {
lev += 1;
- RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
+ iter_lev_in_flags_set(hash, lev);
if (lev == RHASH_LEV_MAX) {
iter_lev_in_ivar_set(hash, lev);
}
@@ -1454,7 +1484,7 @@ hash_iter_lev_dec(VALUE hash)
}
else {
HASH_ASSERT(lev > 0);
- RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((lev-1) << RHASH_LEV_SHIFT));
+ iter_lev_in_flags_set(hash, lev - 1);
}
}
@@ -1533,6 +1563,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
hash_verify(hash);
}
+void rb_st_compact_table(st_table *tab);
+
+static void
+compact_after_delete(VALUE hash)
+{
+ if (RHASH_ITER_LEV(hash) == 0 && RHASH_ST_TABLE_P(hash)) {
+ rb_st_compact_table(RHASH_ST_TABLE(hash));
+ }
+}
+
static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone)
{
@@ -1705,7 +1745,7 @@ rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func,
if (RHASH_AR_TABLE_P(hash)) {
int result = ar_update(hash, key, func, arg);
if (result == -1) {
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
else {
return result;
@@ -2070,13 +2110,27 @@ call_default_proc(VALUE proc, VALUE hash, VALUE key)
return rb_proc_call_with_block(proc, 2, args, Qnil);
}
+static bool
+rb_hash_default_unredefined(VALUE hash)
+{
+ VALUE klass = RBASIC_CLASS(hash);
+ if (LIKELY(klass == rb_cHash)) {
+ return !!BASIC_OP_UNREDEFINED_P(BOP_DEFAULT, HASH_REDEFINED_OP_FLAG);
+ }
+ else {
+ return LIKELY(rb_method_basic_definition_p(klass, id_default));
+ }
+}
+
VALUE
rb_hash_default_value(VALUE hash, VALUE key)
{
- if (LIKELY(rb_method_basic_definition_p(CLASS_OF(hash), id_default))) {
+ RUBY_ASSERT(RB_TYPE_P(hash, T_HASH));
+
+ if (LIKELY(rb_hash_default_unredefined(hash))) {
VALUE ifnone = RHASH_IFNONE(hash);
- if (!FL_TEST(hash, RHASH_PROC_DEFAULT)) return ifnone;
- if (key == Qundef) return Qnil;
+ if (LIKELY(!FL_TEST_RAW(hash, RHASH_PROC_DEFAULT))) return ifnone;
+ if (UNDEF_P(key)) return Qnil;
return call_default_proc(ifnone, hash, key);
}
else {
@@ -2402,7 +2456,7 @@ rb_hash_delete(VALUE hash, VALUE key)
{
VALUE deleted_value = rb_hash_delete_entry(hash, key);
- if (deleted_value != Qundef) { /* likely pass */
+ if (!UNDEF_P(deleted_value)) { /* likely pass */
return deleted_value;
}
else {
@@ -2445,7 +2499,8 @@ rb_hash_delete_m(VALUE hash, VALUE key)
rb_hash_modify_check(hash);
val = rb_hash_delete_entry(hash, key);
- if (val != Qundef) {
+ if (!UNDEF_P(val)) {
+ compact_after_delete(hash);
return val;
}
else {
@@ -2502,7 +2557,7 @@ rb_hash_shift(VALUE hash)
}
else {
rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
- if (var.key != Qundef) {
+ if (!UNDEF_P(var.key)) {
rb_hash_delete_entry(hash, var.key);
return rb_assoc_new(var.key, var.val);
}
@@ -2517,7 +2572,7 @@ rb_hash_shift(VALUE hash)
}
else {
rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
- if (var.key != Qundef) {
+ if (!UNDEF_P(var.key)) {
rb_hash_delete_entry(hash, var.key);
return rb_assoc_new(var.key, var.val);
}
@@ -2566,6 +2621,7 @@ rb_hash_delete_if(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, delete_if_i, hash);
+ compact_after_delete(hash);
}
return hash;
}
@@ -2629,6 +2685,7 @@ rb_hash_reject(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, delete_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2658,7 +2715,7 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash)
for (i = 0; i < argc; i++) {
key = argv[i];
value = rb_hash_lookup2(hash, key, Qundef);
- if (value != Qundef)
+ if (!UNDEF_P(value))
rb_hash_aset(result, key, value);
}
@@ -2688,6 +2745,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
key = argv[i];
rb_hash_delete(result, key);
}
+ compact_after_delete(result);
return result;
}
@@ -2785,6 +2843,7 @@ rb_hash_select(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, keep_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2876,6 +2935,7 @@ rb_hash_clear(VALUE hash)
}
else {
st_clear(RHASH_ST_TABLE(hash));
+ compact_after_delete(hash);
}
return hash;
@@ -2949,7 +3009,7 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val)
ar_alloc_table(hash);
}
- if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) {
+ if (!RHASH_STRING_KEY_P(hash, key)) {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
}
else {
@@ -3181,7 +3241,7 @@ transform_keys_hash_i(VALUE key, VALUE value, VALUE transarg)
struct transform_keys_args *p = (void *)transarg;
VALUE trans = p->trans, result = p->result;
VALUE new_key = rb_hash_lookup2(trans, key, Qundef);
- if (new_key == Qundef) {
+ if (UNDEF_P(new_key)) {
if (p->block_given)
new_key = rb_yield(key);
else
@@ -3302,7 +3362,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
if (!trans) {
new_key = rb_yield(key);
}
- else if ((new_key = rb_hash_lookup2(trans, key, Qundef)) != Qundef) {
+ else if (!UNDEF_P(new_key = rb_hash_lookup2(trans, key, Qundef))) {
/* use the transformed key */
}
else if (block_given) {
@@ -3321,6 +3381,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
rb_ary_clear(pairs);
rb_hash_clear(new_keys);
}
+ compact_after_delete(hash);
return hash;
}
@@ -3371,6 +3432,7 @@ rb_hash_transform_values(VALUE hash)
if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
+ compact_after_delete(result);
}
return result;
@@ -3907,18 +3969,9 @@ rb_hash_invert(VALUE hash)
}
static int
-rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
-{
- *value = arg->arg;
- return ST_CONTINUE;
-}
-
-NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
-
-static int
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
{
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
+ rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
@@ -3930,6 +3983,9 @@ rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_ar
if (existing) {
newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue);
}
+ else if (RHASH_STRING_KEY_P(arg->hash, *key) && !RB_OBJ_FROZEN(*key)) {
+ *key = rb_hash_key_str(*key);
+ }
*value = newvalue;
return ST_CONTINUE;
}
@@ -4182,7 +4238,7 @@ rb_hash_assoc(VALUE hash, VALUE key)
table = RHASH_ST_TABLE(hash);
orighash = table->type;
- if (orighash != &identhash) {
+ if (!RHASH_IDENTHASH_P(hash)) {
VALUE value;
struct reset_hash_type_arg ensure_arg;
struct st_hash_type assochash;
@@ -4195,7 +4251,7 @@ rb_hash_assoc(VALUE hash, VALUE key)
ensure_arg.hash = hash;
ensure_arg.orighash = orighash;
value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
- if (value != Qundef) return rb_assoc_new(key, value);
+ if (!UNDEF_P(value)) return rb_assoc_new(key, value);
}
args[0] = key;
@@ -4442,7 +4498,7 @@ rb_hash_compare_by_id(VALUE hash)
MJIT_FUNC_EXPORTED VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
- return RBOOL(RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash);
+ return RBOOL(RHASH_IDENTHASH_P(hash));
}
VALUE
@@ -4608,7 +4664,7 @@ hash_le_i(VALUE key, VALUE value, VALUE arg)
{
VALUE *args = (VALUE *)arg;
VALUE v = rb_hash_lookup2(args[0], key, Qundef);
- if (v != Qundef && rb_equal(value, v)) return ST_CONTINUE;
+ if (!UNDEF_P(v) && rb_equal(value, v)) return ST_CONTINUE;
args[1] = Qfalse;
return ST_STOP;
}
@@ -4760,7 +4816,7 @@ rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val)
if (ret != -1) {
return ret;
}
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
tbl = RHASH_TBL_RAW(hash);
return st_update(tbl, (st_data_t)key, add_new_i, (st_data_t)args);
@@ -4939,7 +4995,7 @@ env_name(volatile VALUE *s)
static VALUE env_aset(VALUE nm, VALUE val);
static void
-reset_by_modified_env(const char *nam)
+reset_by_modified_env(const char *nam, const char *val)
{
/*
* ENV['TZ'] = nil has a special meaning.
@@ -4948,7 +5004,7 @@ reset_by_modified_env(const char *nam)
* This hack might works only on Linux glibc.
*/
if (ENVMATCH(nam, TZ_ENV)) {
- ruby_reset_timezone();
+ ruby_reset_timezone(val);
}
}
@@ -4956,7 +5012,7 @@ static VALUE
env_delete(VALUE name)
{
const char *nam = env_name(name);
- reset_by_modified_env(nam);
+ reset_by_modified_env(nam, NULL);
VALUE val = getenv_with_lock(nam);
if (!NIL_P(val)) {
@@ -5416,7 +5472,7 @@ env_aset(VALUE nm, VALUE val)
get_env_ptr(value, val);
ruby_setenv(name, value);
- reset_by_modified_env(name);
+ reset_by_modified_env(name, value);
return val;
}
@@ -5971,24 +6027,23 @@ env_to_s(VALUE _)
static VALUE
env_inspect(VALUE _)
{
- VALUE i;
VALUE str = rb_str_buf_new2("{");
+ rb_encoding *enc = env_encoding();
ENV_LOCK();
{
char **env = GET_ENVIRON(environ);
while (*env) {
- char *s = strchr(*env, '=');
+ const char *s = strchr(*env, '=');
if (env != environ) {
rb_str_buf_cat2(str, ", ");
}
if (s) {
- rb_str_buf_cat2(str, "\"");
- rb_str_buf_cat(str, *env, s-*env);
- rb_str_buf_cat2(str, "\"=>");
- i = rb_inspect(rb_str_new2(s+1));
- rb_str_buf_append(str, i);
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(*env, s-*env, enc)));
+ rb_str_buf_cat2(str, "=>");
+ s++;
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(s, strlen(s), enc)));
}
env++;
}
@@ -7164,7 +7219,6 @@ void
Init_Hash(void)
{
id_hash = rb_intern_const("hash");
- id_default = rb_intern_const("default");
id_flatten_bang = rb_intern_const("flatten!");
id_hash_iter_lev = rb_make_internal_id();
diff --git a/hrtime.h b/hrtime.h
index 80aff5deb3..7ed4e6b04c 100644
--- a/hrtime.h
+++ b/hrtime.h
@@ -206,6 +206,7 @@ double2hrtime(rb_hrtime_t *hrt, double d)
const double TIMESPEC_SEC_MAX_PLUS_ONE = 2.0 * (TIMESPEC_SEC_MAX_as_double / 2.0 + 1.0);
if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
+ *hrt = RB_HRTIME_MAX;
return NULL;
}
else if (d <= 0) {
diff --git a/id_table.h b/id_table.h
index 9d9eb5648e..f72e2d1d92 100644
--- a/id_table.h
+++ b/id_table.h
@@ -19,7 +19,6 @@ struct rb_id_table *rb_id_table_create(size_t size);
void rb_id_table_free(struct rb_id_table *tbl);
void rb_id_table_clear(struct rb_id_table *tbl);
-size_t rb_id_table_size(const struct rb_id_table *tbl);
size_t rb_id_table_memsize(const struct rb_id_table *tbl);
int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val);
@@ -33,4 +32,8 @@ void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *fu
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
void rb_id_table_foreach_values_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, rb_id_table_update_value_callback_func_t *replace, void *data);
+RUBY_SYMBOL_EXPORT_BEGIN
+size_t rb_id_table_size(const struct rb_id_table *tbl);
+RUBY_SYMBOL_EXPORT_END
+
#endif /* RUBY_ID_TABLE_H */
diff --git a/include/ruby/assert.h b/include/ruby/assert.h
index c9f2c3fbef..0c052363bc 100644
--- a/include/ruby/assert.h
+++ b/include/ruby/assert.h
@@ -103,7 +103,7 @@
# /* keep NDEBUG undefined */
#elif (RBIMPL_NDEBUG == 0) && (RBIMPL_RUBY_DEBUG == 0)
-# /* The (*1) situation in avobe diagram. */
+# /* The (*1) situation in above diagram. */
# define RUBY_DEBUG 0
# define RUBY_NDEBUG 1
# define NDEBUG
diff --git a/include/ruby/debug.h b/include/ruby/debug.h
index c88da9c43d..f95acdb17e 100644
--- a/include/ruby/debug.h
+++ b/include/ruby/debug.h
@@ -207,6 +207,17 @@ typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void
VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data);
/**
+ * Queries the backtrace object of the context. This is as if you call
+ * `caller_locations` at the point of debugger.
+ *
+ * @param[in] dc A debug context.
+ * @return An array of `Thread::Backtrace::Location` which represents the
+ * current point of execution at `dc`.
+
+ */
+VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
+
+/**
* Queries the current receiver of the passed context's upper frame.
*
* @param[in] dc A debug context.
@@ -250,15 +261,27 @@ VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long
VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
/**
- * Queries the backtrace object of the context. This is as if you call
- * `caller_locations` at the point of debugger.
+ * Queries the depth of the passed context's upper frame.
*
- * @param[in] dc A debug context.
- * @return An array of `Thread::Backtrace::Location` which represents the
- * current point of execution at `dc`.
+ * Note that the depth is not same as the frame index because debug_inspector
+ * skips some special frames but the depth counts all frames.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @retval The depth at `index`-th frame in Integer.
+ */
+VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index);
+
+// A macro to recognize `rb_debug_inspector_frame_depth()` is available or not
+#define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index)
+/**
+ * Return current frmae depth.
+ *
+ * @retval The depth of the current frame in Integer.
*/
-VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
+VALUE rb_debug_inspector_current_depth(void);
/** @} */
diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h
index 9f67bd5bf6..250b39b6df 100644
--- a/include/ruby/fiber/scheduler.h
+++ b/include/ruby/fiber/scheduler.h
@@ -23,6 +23,8 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
+#define RUBY_FIBER_SCHEDULER_VERSION 2
+
struct timeval;
/**
@@ -144,7 +146,7 @@ VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout);
VALUE rb_fiber_scheduler_close(VALUE scheduler);
/**
- * Nonblocking `sleep`. Depending on scheduler implementation, this for
+ * Non-blocking `sleep`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
@@ -172,7 +174,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
#endif
/**
- * Nonblocking `waitpid`. Depending on scheduler implementation, this for
+ * Non-blocking `waitpid`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
@@ -183,7 +185,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags);
/**
- * Nonblocking wait for the passed "blocker", which is for instance
+ * Non-blocking wait for the passed "blocker", which is for instance
* `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this
* for instance switches to another fiber etc.
*
@@ -205,8 +207,8 @@ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout);
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
/**
- * Nonblocking version of rb_io_wait(). Depending on scheduler implementation,
- * this for instance switches to another fiber etc.
+ * Non-blocking version of rb_io_wait(). Depending on scheduler
+ * implementation, this for instance switches to another fiber etc.
*
* The "events" here is a Ruby level integer, which is an OR-ed value of
* `IO::READABLE`, `IO::WRITABLE`, and `IO::PRIORITY`.
@@ -220,7 +222,7 @@ VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout);
/**
- * Nonblocking wait until the passed IO is ready for reading. This is a
+ * Non-blocking wait until the passed IO is ready for reading. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::READABLE` and timeout is never.
*
@@ -231,7 +233,7 @@ VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE
VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
/**
- * Nonblocking wait until the passed IO is ready for writing. This is a
+ * Non-blocking wait until the passed IO is ready for writing. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::WRITABLE` and timeout is never.
*
@@ -242,57 +244,81 @@ VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io);
/**
- * Nonblocking read from the passed IO.
+ * Non-blocking version of `IO.select`.
+ *
+ * It's possible that this will be emulated using a thread, so you should not
+ * rely on it for high performance.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] readables An array of readable objects.
+ * @param[in] writables An array of writable objects.
+ * @param[in] exceptables An array of objects that might encounter exceptional conditions.
+ * @param[in] timeout Numeric timeout or nil.
+ * @return What `scheduler.io_select` returns, normally a 3-tuple of arrays of ready objects.
+ */
+VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout);
+
+/**
+ * Non-blocking version of `IO.select`, `argv` variant.
+ */
+VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv);
+
+/**
+ * Non-blocking read from the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
* @param[out] buffer Return buffer.
* @param[in] length Requested number of bytes to read.
+ * @param[in] offset The offset in the buffer to read to.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns `[-errno, size]`.
*/
-VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
+VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking write to the passed IO.
+ * Non-blocking write to the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
* @param[in] buffer What to write.
* @param[in] length Number of bytes to write.
+ * @param[in] offset The offset in the buffer to write from.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns `[-errno, size]`.
*/
-VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
+VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking read from the passed IO at the specified offset.
+ * Non-blocking read from the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
- * @param[out] buffer Return buffer.
+ * @param[in] from The offset in the given IO to read the data from.
+ * @param[out] buffer The buffer to read the data to.
* @param[in] length Requested number of bytes to read.
- * @param[in] offset The offset in the given IO to read the data from.
+ * @param[in] offset The offset in the buffer to read to.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
-VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
+VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking write to the passed IO at the specified offset.
+ * Non-blocking write to the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
- * @param[in] buffer What to write.
+ * @param[in] from The offset in the given IO to write the data to.
+ * @param[in] buffer The buffer to write the data from.
* @param[in] length Number of bytes to write.
- * @param[in] offset The offset in the given IO to write the data to.
+ * @param[in] offset The offset in the buffer to write from.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
-VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
+VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
/**
- * Nonblocking read from the passed IO using a native buffer.
+ * Non-blocking read from the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
@@ -305,7 +331,7 @@ VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size
VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, size_t size, size_t length);
/**
- * Nonblocking write to the passed IO using a native buffer.
+ * Non-blocking write to the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
@@ -318,7 +344,7 @@ VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer,
VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *buffer, size_t size, size_t length);
/**
- * Nonblocking close the given IO.
+ * Non-blocking close the given IO.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to close.
@@ -328,7 +354,7 @@ VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *
VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
/**
- * Nonblocking DNS lookup.
+ * Non-blocking DNS lookup.
*
* @param[in] scheduler Target scheduler.
* @param[in] hostname A host name to query.
@@ -337,6 +363,12 @@ VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
*/
VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname);
+/**
+ * Create and schedule a non-blocking fiber.
+ *
+ */
+VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_FIBER_SCHEDULER_H */
diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h
index fe1977a9a1..44111a0055 100644
--- a/include/ruby/internal/abi.h
+++ b/include/ruby/internal/abi.h
@@ -24,13 +24,14 @@
* In released versions of Ruby, this number is not defined since teeny
* versions of Ruby should guarantee ABI compatibility.
*/
-#define RUBY_ABI_VERSION 2
+#define RUBY_ABI_VERSION 3
/* Windows does not support weak symbols so ruby_abi_version will not exist
* in the shared library. */
#if defined(HAVE_FUNC_WEAK) && !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
# define RUBY_DLN_CHECK_ABI
#endif
+#endif /* RUBY_ABI_VERSION */
#ifdef RUBY_DLN_CHECK_ABI
@@ -41,7 +42,11 @@ extern "C" {
RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak))
ruby_abi_version(void)
{
+# ifdef RUBY_ABI_VERSION
return RUBY_ABI_VERSION;
+# else
+ return 0;
+# endif
}
# ifdef __cplusplus
@@ -51,5 +56,3 @@ ruby_abi_version(void)
#endif
#endif
-
-#endif
diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h
index 9d8d16fdab..e4c6d155cc 100644
--- a/include/ruby/internal/anyargs.h
+++ b/include/ruby/internal/anyargs.h
@@ -84,12 +84,15 @@
#elif defined(_WIN32) || defined(__CYGWIN__)
# /* Skip due to [Bug #16134] */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! defined(HAVE_VA_ARGS_MACRO)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#else
# /** @cond INTERNAL_MACRO */
@@ -239,15 +242,16 @@
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n))
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n))
# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n))
-# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n))
# define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym)
# define RBIMPL_ANYARGS_DECL(sym, ...) \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \
RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \
@@ -347,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *)
#endif /* __cplusplus */
+#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus)
+/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became
+ * equivalent to `void foo(void)` unlike in earlier versions. This is a problem
+ * for rb_define_* functions since that makes all valid functions one can pass
+ * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly
+ * not a problem for the __builtin_choose_expr path, but outside of that we
+ * need to add a cast for compatibility.
+ */
+#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity))
+
+#undef RBIMPL_CAST_FN_PTR
+#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */
+
/**
* This macro is to properly cast a function parameter of *_define_method
* family. It has been around since 1.x era so you can maximise backwards
diff --git a/include/ruby/internal/arithmetic.h b/include/ruby/internal/arithmetic.h
index 3f7840c384..7ebb4a86f1 100644
--- a/include/ruby/internal/arithmetic.h
+++ b/include/ruby/internal/arithmetic.h
@@ -18,7 +18,8 @@
* Do not expect for instance `__VA_ARGS__` is always available.
* We assume C99 for ruby itself but we don't assume languages of
* extension libraries. They could be written in C++98.
- * @brief Conversion between C's arithmtic types and Ruby's numeric types.
+ * @brief Conversion between C's arithmetic types and Ruby's numeric
+ * types.
*/
#include "ruby/internal/arithmetic/char.h"
#include "ruby/internal/arithmetic/double.h"
diff --git a/include/ruby/internal/attr/nodiscard.h b/include/ruby/internal/attr/nodiscard.h
index 087192a7a8..c3ae118942 100644
--- a/include/ruby/internal/attr/nodiscard.h
+++ b/include/ruby/internal/attr/nodiscard.h
@@ -26,7 +26,7 @@
/**
* Wraps (or simulates) `[[nodiscard]]`. In C++ (at least since C++20) a
- * nodiscard attribute can have a message why the result shall not be ignoed.
+ * nodiscard attribute can have a message why the result shall not be ignored.
* However GCC attribute and SAL annotation cannot take them.
*/
#if RBIMPL_HAS_CPP_ATTRIBUTE(nodiscard)
diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h
new file mode 100644
index 0000000000..de26e926d4
--- /dev/null
+++ b/include/ruby/internal/attr/nonstring.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NONSTRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NONSTRING.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((nonstring))` */
+#if RBIMPL_HAS_ATTRIBUTE(nonstring)
+# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring))
+#else
+# define RBIMPL_ATTR_NONSTRING() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NONSTRING_H */
diff --git a/include/ruby/internal/config.h b/include/ruby/internal/config.h
index 0c434e5b05..aa63376d7c 100644
--- a/include/ruby/internal/config.h
+++ b/include/ruby/internal/config.h
@@ -113,6 +113,8 @@
# define UNALIGNED_WORD_ACCESS 1
#elif defined(__powerpc64__)
# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__POWERPC__) // __POWERPC__ is defined for ppc and ppc64 on Darwin
+# define UNALIGNED_WORD_ACCESS 1
#elif defined(__aarch64__)
# define UNALIGNED_WORD_ACCESS 1
#elif defined(__mc68020__)
diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h
index 7823061d8f..b1c2e1b0a9 100644
--- a/include/ruby/internal/core/robject.h
+++ b/include/ruby/internal/core/robject.h
@@ -37,16 +37,15 @@
/**
* Convenient casting macro.
*
- * @param obj An object, which is in fact an ::RRegexp.
- * @return The passed object casted to ::RRegexp.
+ * @param obj An object, which is in fact an ::RObject.
+ * @return The passed object casted to ::RObject.
*/
#define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj))
/** @cond INTERNAL_MACRO */
#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX
#define ROBJECT_EMBED ROBJECT_EMBED
-#define ROBJECT_NUMIV ROBJECT_NUMIV
+#define ROBJECT_IV_CAPACITY ROBJECT_IV_CAPACITY
#define ROBJECT_IVPTR ROBJECT_IVPTR
-#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL
/** @endcond */
/**
@@ -97,14 +96,6 @@ struct RObject {
/** Basic part, including flags and class. */
struct RBasic basic;
-#if USE_RVARGC
- /**
- * Number of instance variables. This is per object; objects might
- * differ in this field even if they have the identical classes.
- */
- uint32_t numiv;
-#endif
-
/** Object's specific fields. */
union {
@@ -113,14 +104,6 @@ struct RObject {
* this pattern.
*/
struct {
-#if !USE_RVARGC
- /**
- * Number of instance variables. This is per object; objects might
- * differ in this field even if they have the identical classes.
- */
- uint32_t numiv;
-#endif
-
/** Pointer to a C array that holds instance variables. */
VALUE *ivptr;
@@ -132,7 +115,7 @@ struct RObject {
*
* This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`.
*/
- struct st_table *iv_index_tbl;
+ struct rb_id_table *iv_index_tbl;
} heap;
#if USE_RVARGC
@@ -157,11 +140,6 @@ struct RObject {
/* Offsets for YJIT */
#ifndef __cplusplus
-# if USE_RVARGC
-static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, numiv);
-# else
-static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, as.heap.numiv);
-# endif
static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr);
static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl);
static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
@@ -170,32 +148,6 @@ static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * Queries the number of instance variables.
- *
- * @param[in] obj Object in question.
- * @return Its number of instance variables.
- * @pre `obj` must be an instance of ::RObject.
- */
-static inline uint32_t
-ROBJECT_NUMIV(VALUE obj)
-{
- RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
-
-#if USE_RVARGC
- return ROBJECT(obj)->numiv;
-#else
- if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
- return ROBJECT_EMBED_LEN_MAX;
- }
- else {
- return ROBJECT(obj)->as.heap.numiv;
- }
-#endif
-}
-
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
* Queries the instance variables.
*
* @param[in] obj Object in question.
diff --git a/include/ruby/internal/eval.h b/include/ruby/internal/eval.h
index 34a53849da..5bcbb97746 100644
--- a/include/ruby/internal/eval.h
+++ b/include/ruby/internal/eval.h
@@ -28,10 +28,12 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NONNULL(())
/**
- * Evaluates the given string in an isolated binding.
+ * Evaluates the given string.
*
- * Here "isolated" means that the binding does not inherit any other
- * bindings. This behaves same as the binding for required libraries.
+ * In case it is called from within a C-backended method, the evaluation is
+ * done under the current binding. However there can be no method. On such
+ * situation this function evaluates in an isolated binding, like `require`
+ * runs in a separate one.
*
* `__FILE__` will be `"(eval)"`, and `__LINE__` starts from 1 in the
* evaluation.
@@ -39,6 +41,31 @@ RBIMPL_ATTR_NONNULL(())
* @param[in] str Ruby code to evaluate.
* @exception rb_eException Raises an exception on error.
* @return The evaluated result.
+ *
+ * @internal
+ *
+ * @shyouhei's old tale about the birth and growth of this function:
+ *
+ * At the beginning, there was no rb_eval_string(). @shyouhei heard that
+ * @shugo, author of Apache httpd's mod_ruby module, requested @matz for this
+ * API. He wanted a way so that mod_ruby can evaluate ruby scripts one by one,
+ * separately, in each different contexts. So this function was made. It was
+ * designed to be a global interpreter entry point like ruby_run_node().
+ *
+ * The way it is implemented however allows extension libraries (not just
+ * programs like Apache httpd) to call this function. Because its name says
+ * nothing about the initial design, people started to think of it as an
+ * orthodox way to call ruby level `eval` method from their extension
+ * libraries. Even our `extension.rdoc` has had a description of this function
+ * basically according to this understanding.
+ *
+ * The old (mod_ruby like) usage still works. But over time, usages of this
+ * function from extension libraries got popular, while mod_ruby faded out; is
+ * no longer maintained now. Devs decided to actively support both. This
+ * function now auto-detects how it is called, and switches how it works
+ * depending on it.
+ *
+ * @see https://bugs.ruby-lang.org/issues/18780
*/
VALUE rb_eval_string(const char *str);
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
index c51bd2e9d9..7383426b23 100644
--- a/include/ruby/internal/fl_type.h
+++ b/include/ruby/internal/fl_type.h
@@ -941,21 +941,8 @@ RB_OBJ_FREEZE_RAW(VALUE obj)
RB_FL_SET_RAW(obj, RUBY_FL_FREEZE);
}
-/**
- * Prevents further modifications to the given object. ::rb_eFrozenError shall
- * be raised if modification is attempted.
- *
- * @param[out] x Object in question.
- */
-static inline void
-rb_obj_freeze_inline(VALUE x)
-{
- if (RB_FL_ABLE(x)) {
- RB_OBJ_FREEZE_RAW(x);
- if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) {
- rb_freeze_singleton_class(x);
- }
- }
-}
+RUBY_SYMBOL_EXPORT_BEGIN
+void rb_obj_freeze_inline(VALUE obj);
+RUBY_SYMBOL_EXPORT_END
#endif /* RBIMPL_FL_TYPE_H */
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
index 66fc14e511..054e4b0f9c 100644
--- a/include/ruby/internal/gc.h
+++ b/include/ruby/internal/gc.h
@@ -26,10 +26,15 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
- * Inform the garbage collector that `valptr` points to a live Ruby object that
- * should not be moved. Note that extensions should use this API on global
- * constants instead of assuming constants defined in Ruby are always alive.
- * Ruby code can remove global constants.
+ * Inform the garbage collector that the global or static variable pointed by
+ * `valptr` stores a live Ruby object that should not be moved. Note that
+ * extensions should use this API on global constants instead of assuming
+ * constants defined in Ruby are always alive. Ruby code can remove global
+ * constants.
+ *
+ * Because this registration itself has a possibility to trigger a GC, this
+ * function must be called before any GC-able objects is assigned to the
+ * address pointed by `valptr`.
*/
void rb_gc_register_address(VALUE *valptr);
diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h
index 2181ab93c7..0fb2d001bc 100644
--- a/include/ruby/internal/intern/class.h
+++ b/include/ruby/internal/intern/class.h
@@ -200,6 +200,18 @@ VALUE rb_class_descendants(VALUE klass);
*/
VALUE rb_class_subclasses(VALUE klass);
+
+/**
+ * Returns the attached object for a singleton class.
+ * If the given class is not a singleton class, raises a TypeError.
+ *
+ * @param[in] klass A class.
+ * @return The object which has the singleton class `klass`.
+ *
+ * @internal
+ */
+VALUE rb_class_attached_object(VALUE klass);
+
/**
* Generates an array of symbols, which are the list of method names defined in
* the passed class.
diff --git a/include/ruby/internal/intern/cont.h b/include/ruby/internal/intern/cont.h
index 37493009f5..32647f48aa 100644
--- a/include/ruby/internal/intern/cont.h
+++ b/include/ruby/internal/intern/cont.h
@@ -39,6 +39,28 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
VALUE rb_fiber_new(rb_block_call_func_t func, VALUE callback_obj);
/**
+ * Creates a Fiber instance from a C-backended block with the specified
+ * storage.
+ *
+ * If the given storage is Qundef or Qtrue, this function is equivalent to
+ * rb_fiber_new() which inherits storage from the current fiber.
+ *
+ * Specifying Qtrue is experimental and may be changed in the future.
+ *
+ * If the given storage is Qnil, this function will lazy initialize the
+ * internal storage which starts of empty (without any inheritance).
+ *
+ * Otherwise, the given storage is used as the internal storage.
+ *
+ * @param[in] func A function, to become the fiber's body.
+ * @param[in] callback_obj Passed as-is to `func`.
+ * @param[in] storage The way to set up the storage for the fiber.
+ * @return An allocated new instance of rb_cFiber, which is ready to be
+ * "resume"d.
+ */
+VALUE rb_fiber_new_storage(rb_block_call_func_t func, VALUE callback_obj, VALUE storage);
+
+/**
* Queries the fiber which is calling this function. Any ruby execution
* context has its fiber, either explicitly or implicitly.
*
diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h
index 2dc60c7ba7..79820fdc61 100644
--- a/include/ruby/internal/intern/file.h
+++ b/include/ruby/internal/intern/file.h
@@ -206,7 +206,7 @@ int rb_is_absolute_path(const char *path);
* unpredictable. POSIX's `<sys/stat.h>` states that "the use of
* this field is unspecified" then.
*/
-off_t rb_file_size(VALUE file);
+rb_off_t rb_file_size(VALUE file);
RBIMPL_SYMBOL_EXPORT_END()
diff --git a/include/ruby/internal/intern/gc.h b/include/ruby/internal/intern/gc.h
index e7b8008729..2ee1d257db 100644
--- a/include/ruby/internal/intern/gc.h
+++ b/include/ruby/internal/intern/gc.h
@@ -26,7 +26,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
@@ -71,7 +71,7 @@ RBIMPL_ATTR_NONNULL((1))
* addressable.
* @param[out] start Pointer to an array of objects.
* @param[out] end Pointer that terminates the array of objects.
- * @post Objects from `start` to `end`, both inclusive, are marked.
+ * @post Objects from `start` (included) to `end` (excluded) are marked.
*
* @internal
*
diff --git a/include/ruby/internal/intern/object.h b/include/ruby/internal/intern/object.h
index 19af49b140..b9ffa57c06 100644
--- a/include/ruby/internal/intern/object.h
+++ b/include/ruby/internal/intern/object.h
@@ -92,8 +92,8 @@ VALUE rb_class_new_instance_kw(int argc, const VALUE *argv, VALUE klass, int kw_
*
* @param[in] lhs Comparison left hand side.
* @param[in] rhs Comparison right hand side.
- * @retval RUBY_Qtrue They are equal.
- * @retval RUBY_Qfalse Otherwise.
+ * @retval non-zero They are equal.
+ * @retval 0 Otherwise.
* @note This function actually calls `lhs.eql?(rhs)` so you cannot
* implement your class' `#eql?` method using it.
*/
diff --git a/include/ruby/internal/intern/select/posix.h b/include/ruby/internal/intern/select/posix.h
index 5f828e66e2..0a9b0b2e51 100644
--- a/include/ruby/internal/intern/select/posix.h
+++ b/include/ruby/internal/intern/select/posix.h
@@ -136,7 +136,7 @@ rb_fd_max(const rb_fdset_t *f)
}
/** @cond INTERNAL_MACRO */
-/* :FIXME: What are these? They don't exist for shibling implementations. */
+/* :FIXME: What are these? They don't exist for sibling implementations. */
#define rb_fd_init_copy(d, s) (*(d) = *(s))
#define rb_fd_term(f) ((void)(f))
/** @endcond */
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h
index 0f59262a91..6884db195d 100644
--- a/include/ruby/internal/memory.h
+++ b/include/ruby/internal/memory.h
@@ -287,12 +287,12 @@ typedef uint128_t DSIZE_T;
RBIMPL_CAST((type *)alloca(rbimpl_size_mul_or_raise(sizeof(type), (n))))
/**
- * Identical to #RB_ALLOCV_N(), except it implicitly assumes the type of array
- * is ::VALUE.
+ * Identical to #RB_ALLOCV_N(), except that it allocates a number of bytes and
+ * returns a void* .
*
* @param v A variable to hold the just-in-case opaque Ruby object.
* @param n Size of allocation, in bytes.
- * @return An array of `n` bytes of ::VALUE.
+ * @return A void pointer to `n` bytes storage.
* @note `n` may be evaluated twice.
*/
#define RB_ALLOCV(v, n) \
diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h
index cf5b18f77d..1ed2bf6368 100644
--- a/include/ruby/internal/scan_args.h
+++ b/include/ruby/internal/scan_args.h
@@ -100,7 +100,7 @@ RBIMPL_ATTR_NONNULL((2, 3))
* param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec /
* pre-opt-post-arg-spec
* pre-arg-spec := num-of-leading-mandatory-args
- [num-of-optional-args]
+ * [num-of-optional-args]
* post-arg-spec := sym-for-variable-length-args
* [num-of-trailing-mandatory-args]
* pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args
diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h
index 38934e4da3..dc0a6b41d6 100644
--- a/include/ruby/internal/special_consts.h
+++ b/include/ruby/internal/special_consts.h
@@ -76,6 +76,8 @@
#define RB_SPECIAL_CONST_P RB_SPECIAL_CONST_P
#define RB_STATIC_SYM_P RB_STATIC_SYM_P
#define RB_TEST RB_TEST
+#define RB_UNDEF_P RB_UNDEF_P
+#define RB_NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P
/** @endcond */
/** special constants - i.e. non-zero and non-fixnum constants */
@@ -94,9 +96,9 @@ ruby_special_consts {
RUBY_SYMBOL_FLAG, /**< Flag to denote a static symbol. */
#elif USE_FLONUM
RUBY_Qfalse = 0x00, /* ...0000 0000 */
+ RUBY_Qnil = 0x04, /* ...0000 0100 */
RUBY_Qtrue = 0x14, /* ...0001 0100 */
- RUBY_Qnil = 0x08, /* ...0000 1000 */
- RUBY_Qundef = 0x34, /* ...0011 0100 */
+ RUBY_Qundef = 0x24, /* ...0010 0100 */
RUBY_IMMEDIATE_MASK = 0x07, /* ...0000 0111 */
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x03, /* ...0000 0011 */
@@ -104,14 +106,14 @@ ruby_special_consts {
RUBY_SYMBOL_FLAG = 0x0c, /* ...xxxx 1100 */
#else
RUBY_Qfalse = 0x00, /* ...0000 0000 */
- RUBY_Qtrue = 0x02, /* ...0000 0010 */
- RUBY_Qnil = 0x04, /* ...0000 0100 */
- RUBY_Qundef = 0x06, /* ...0000 0110 */
+ RUBY_Qnil = 0x02, /* ...0000 0010 */
+ RUBY_Qtrue = 0x06, /* ...0000 0110 */
+ RUBY_Qundef = 0x0a, /* ...0000 1010 */
RUBY_IMMEDIATE_MASK = 0x03, /* ...0000 0011 */
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */
RUBY_FLONUM_FLAG = 0x02, /* ...0000 0010 */
- RUBY_SYMBOL_FLAG = 0x0e, /* ...0000 1110 */
+ RUBY_SYMBOL_FLAG = 0x0e, /* ...xxxx 1110 */
#endif
RUBY_SPECIAL_SHIFT = 8 /**< Least significant 8 bits are reserved. */
@@ -136,12 +138,21 @@ static inline bool
RB_TEST(VALUE obj)
{
/*
+ * if USE_FLONUM
* Qfalse: ....0000 0000
- * Qnil: ....0000 1000
- * ~Qnil: ....1111 0111
+ * Qnil: ....0000 0100
+ * ~Qnil: ....1111 1011
* v ....xxxx xxxx
* ----------------------------
- * RTEST(v) ....xxxx 0xxx
+ * RTEST(v) ....xxxx x0xx
+ *
+ * if ! USE_FLONUM
+ * Qfalse: ....0000 0000
+ * Qnil: ....0000 0010
+ * ~Qnil: ....1111 1101
+ * v ....xxxx xxxx
+ * ----------------------------
+ * RTEST(v) ....xxxx xx0x
*
* RTEST(v) can be 0 if and only if (v == Qfalse || v == Qnil).
*/
@@ -168,6 +179,62 @@ RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
+ * Checks if the given object is undef.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_UNDEF_P(VALUE obj)
+{
+ return obj == RUBY_Qundef;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is nil or undef. Can be used to see if
+ * a keyword argument is not given or given `nil`.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qnil or ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_NIL_OR_UNDEF_P(VALUE obj)
+{
+ /*
+ * if USE_FLONUM
+ * Qundef: ....0010 0100
+ * Qnil: ....0000 0100
+ * mask: ....1101 1111
+ * common_bits: ....0000 0100
+ * ---------------------------------
+ * Qnil & mask ....0000 0100
+ * Qundef & mask ....0000 0100
+ *
+ * if ! USE_FLONUM
+ * Qundef: ....0000 1010
+ * Qnil: ....0000 0010
+ * mask: ....1111 0111
+ * common_bits: ....0000 0010
+ * ----------------------------
+ * Qnil & mask ....0000 0010
+ * Qundef & mask ....0000 0010
+ *
+ * NIL_OR_UNDEF_P(v) can be true only when v is Qundef or Qnil.
+ */
+ const VALUE mask = ~(RUBY_Qundef ^ RUBY_Qnil);
+ const VALUE common_bits = RUBY_Qundef & RUBY_Qnil;
+ return (obj & mask) == common_bits;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
* Checks if the given object is a so-called Fixnum.
*
* @param[in] obj An arbitrary ruby object.
@@ -259,7 +326,7 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline bool
RB_SPECIAL_CONST_P(VALUE obj)
{
- return RB_IMMEDIATE_P(obj) || ! RB_TEST(obj);
+ return RB_IMMEDIATE_P(obj) || obj == RUBY_Qfalse;
}
RBIMPL_ATTR_CONST()
diff --git a/include/ruby/io.h b/include/ruby/io.h
index dc4c8becf6..88029b1bb9 100644
--- a/include/ruby/io.h
+++ b/include/ruby/io.h
@@ -58,12 +58,20 @@
// IO#wait, IO#wait_readable, IO#wait_writable, IO#wait_priority are defined by this implementation.
#define RUBY_IO_WAIT_METHODS
+// Used as the default timeout argument to `rb_io_wait` to use the `IO#timeout` value.
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
struct stat;
struct timeval;
/**
+ * Indicates that a timeout has occurred while performing an IO operation.
+ */
+RUBY_EXTERN VALUE rb_eIOTimeoutError;
+
+/**
* Type of events that an IO can wait.
*
* @internal
@@ -214,6 +222,11 @@ typedef struct rb_io_t {
* This of course doesn't help inter-process IO interleaves, though.
*/
VALUE write_lock;
+
+ /**
+ * The timeout associated with this IO when performing blocking operations.
+ */
+ VALUE timeout;
} rb_io_t;
/** @alias{rb_io_enc_t} */
@@ -845,13 +858,37 @@ int rb_io_wait_writable(int fd);
int rb_wait_for_single_fd(int fd, int events, struct timeval *tv);
/**
+ * Get the timeout associated with the specified io object.
+ *
+ * @param[in] io An IO object.
+ * @retval RUBY_Qnil There is no associated timeout.
+ * @retval Otherwise The timeout value.
+ */
+VALUE rb_io_timeout(VALUE io);
+
+/**
+ * Set the timeout associated with the specified io object. This timeout is
+ * used as a best effort timeout to prevent operations from blocking forever.
+ *
+ * @param[in] io An IO object.
+ * @param[in] timeout A timeout value. Must respond to #to_f.
+ * @
+ */
+VALUE rb_io_set_timeout(VALUE io, VALUE timeout);
+
+/**
* Blocks until the passed IO is ready for the passed events. The "events"
* here is a Ruby level integer, which is an OR-ed value of `IO::READABLE`,
* `IO::WRITable`, and `IO::PRIORITY`.
*
+ * If timeout is `Qnil`, it will use the default timeout as given by
+ * `rb_io_timeout(io)`.
+ *
* @param[in] io An IO object to wait.
* @param[in] events See above.
* @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * If Qnil, use the default timeout. If Qfalse
+ * or Qundef, wait forever.
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
@@ -903,13 +940,8 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout);
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
- * @exception rb_eTypeError Operation timed out.
- * @return Always returns ::RUBY_IO_READABLE.
- *
- * @internal
- *
- * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function
- * fails to convert that value to `int`, and raises ::rb_eTypeError.
+ * @retval 0 Operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_READABLE.
*/
int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
@@ -924,13 +956,8 @@ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
* @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason.
- * @exception rb_eTypeError Operation timed out.
- * @return Always returns ::RUBY_IO_WRITABLE.
- *
- * @internal
- *
- * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function
- * fails to convert that value to `int`, and raises ::rb_eTypeError.
+ * @retval 0 Operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_WRITABLE.
*/
int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout);
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
index 907fec20bb..e4b855d8e7 100644
--- a/include/ruby/io/buffer.h
+++ b/include/ruby/io/buffer.h
@@ -1,5 +1,5 @@
-#ifndef RUBY_IO_BUFFER_T
-#define RUBY_IO_BUFFER_T 1
+#ifndef RUBY_IO_BUFFER_H
+#define RUBY_IO_BUFFER_H
/**
* @file
* @author Samuel Williams
@@ -21,6 +21,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
// WARNING: This entire interface is experimental and may change in the future!
#define RB_IO_BUFFER_EXPERIMENTAL 1
+#define RUBY_IO_BUFFER_VERSION 2
+
RUBY_EXTERN VALUE rb_cIOBuffer;
RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE;
RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
@@ -35,6 +37,9 @@ enum rb_io_buffer_flags {
// A non-private mapping is marked as external.
RB_IO_BUFFER_MAPPED = 4,
+ // A mapped buffer that is also shared.
+ RB_IO_BUFFER_SHARED = 8,
+
// The buffer is locked and cannot be resized.
// More specifically, it means we can't change the base address or size.
// A buffer is typically locked before a system call that uses the data.
@@ -51,21 +56,17 @@ enum rb_io_buffer_endian {
RB_IO_BUFFER_LITTLE_ENDIAN = 4,
RB_IO_BUFFER_BIG_ENDIAN = 8,
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#if defined(WORDS_BIGENDIAN)
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
-#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#else
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif REG_DWORD == REG_DWORD_BIG_ENDIAN
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
#endif
RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN
};
VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags);
-VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags);
+VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags);
VALUE rb_io_buffer_lock(VALUE self);
VALUE rb_io_buffer_unlock(VALUE self);
@@ -81,11 +82,11 @@ void rb_io_buffer_resize(VALUE self, size_t size);
void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length);
// The length is the minimum required length.
-VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length);
-VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset);
-VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length);
-VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset);
+VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
+VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
RBIMPL_SYMBOL_EXPORT_END()
-#endif /* RUBY_IO_BUFFER_T */
+#endif /* RUBY_IO_BUFFER_H */
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
index 83931038a0..1ddca2d46f 100644
--- a/include/ruby/memory_view.h
+++ b/include/ruby/memory_view.h
@@ -16,7 +16,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
index a7ef59c7c8..8d7c601703 100644
--- a/include/ruby/onigmo.h
+++ b/include/ruby/onigmo.h
@@ -356,9 +356,9 @@ int onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, c
#define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc)
ONIG_EXTERN
-int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+int onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
-#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen_approximate(p,e,enc)
+#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen(p,e,enc)
#define ONIGENC_MBC_MAXLEN(enc) ((enc)->max_enc_len)
#define ONIGENC_MBC_MAXLEN_DIST(enc) ONIGENC_MBC_MAXLEN(enc)
#define ONIGENC_MBC_MINLEN(enc) ((enc)->min_enc_len)
@@ -744,6 +744,8 @@ typedef struct {
typedef struct {
int lower;
int upper;
+ long base_num;
+ long inner_num;
} OnigRepeatRange;
typedef void (*OnigWarnFunc)(const char* s);
@@ -852,6 +854,8 @@ OnigPosition onig_search_gpos(OnigRegex, const OnigUChar* str, const OnigUChar*
ONIG_EXTERN
OnigPosition onig_match(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option);
ONIG_EXTERN
+int onig_check_linear_time(OnigRegex reg);
+ONIG_EXTERN
OnigRegion* onig_region_new(void);
ONIG_EXTERN
void onig_region_init(OnigRegion* region);
diff --git a/include/ruby/random.h b/include/ruby/random.h
index 657b37f034..39bdb6f3e3 100644
--- a/include/ruby/random.h
+++ b/include/ruby/random.h
@@ -16,6 +16,26 @@
#include "ruby/ruby.h"
+/*
+ * version
+ * 0: before versioning; deprecated
+ * 1: added version, flags and init_32bit function
+ */
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 1
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR 0
+
+#define RUBY_RANDOM_PASTE_VERSION_SUFFIX(x, y, z) x##_##y##_##z
+#define RUBY_RANDOM_WITH_VERSION_SUFFIX(name, major, minor) \
+ RUBY_RANDOM_PASTE_VERSION_SUFFIX(name, major, minor)
+#define rb_random_data_type \
+ RUBY_RANDOM_WITH_VERSION_SUFFIX(rb_random_data_type, \
+ RUBY_RANDOM_INTERFACE_VERSION_MAJOR, \
+ RUBY_RANDOM_INTERFACE_VERSION_MINOR)
+#define RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER \
+ {RUBY_RANDOM_INTERFACE_VERSION_MAJOR, RUBY_RANDOM_INTERFACE_VERSION_MINOR}
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX 0xff
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR_MAX 0xff
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
@@ -48,6 +68,17 @@ typedef void rb_random_init_func(rb_random_t *rng, const uint32_t *buf, size_t l
RBIMPL_ATTR_NONNULL(())
/**
+ * This is the type of functions called when your random object is initialised.
+ * Passed data is the seed integer.
+ *
+ * @param[out] rng Your random struct to fill in.
+ * @param[in] data Seed, single word.
+ * @post `rng` is initialised using the passed seeds.
+ */
+typedef void rb_random_init_int32_func(rb_random_t *rng, uint32_t data);
+
+RBIMPL_ATTR_NONNULL(())
+/**
* This is the type of functions called from your object's `#rand` method.
*
* @param[out] rng Your random struct to extract an integer from.
@@ -84,9 +115,24 @@ typedef struct {
/** Number of bits of seed numbers. */
size_t default_seed_bits;
- /** Initialiser function. */
+ /**
+ * Major/minor versions of this interface
+ */
+ struct {
+ uint8_t major, minor;
+ } version;
+
+ /**
+ * Reserved flags
+ */
+ uint16_t flags;
+
+ /** Function to initialize from uint32_t array. */
rb_random_init_func *init;
+ /** Function to initialize from single uint32_t. */
+ rb_random_init_int32_func *init_int32;
+
/** Function to obtain a random integer. */
rb_random_get_int32_func *get_int32;
@@ -130,11 +176,12 @@ typedef struct {
} rb_random_interface_t;
/**
- * This utility macro defines 3 functions named prefix_init, prefix_get_int32,
- * prefix_get_bytes.
+ * This utility macro defines 4 functions named prefix_init, prefix_init_int32,
+ * prefix_get_int32, prefix_get_bytes.
*/
#define RB_RANDOM_INTERFACE_DECLARE(prefix) \
static void prefix##_init(rb_random_t *, const uint32_t *, size_t); \
+ static void prefix##_init_int32(rb_random_t *, uint32_t); \
static unsigned int prefix##_get_int32(rb_random_t *); \
static void prefix##_get_bytes(rb_random_t *, void *, size_t)
@@ -161,7 +208,9 @@ typedef struct {
* ```
*/
#define RB_RANDOM_INTERFACE_DEFINE(prefix) \
+ RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER, 0, \
prefix##_init, \
+ prefix##_init_int32, \
prefix##_get_int32, \
prefix##_get_bytes
@@ -173,6 +222,12 @@ typedef struct {
RB_RANDOM_INTERFACE_DEFINE(prefix), \
prefix##_get_real
+#define RB_RANDOM_DEFINE_INIT_INT32_FUNC(prefix) \
+ static void prefix##_init_int32(rb_random_t *rnd, uint32_t data) \
+ { \
+ prefix##_init(rnd, &data, 1); \
+ }
+
#if defined _WIN32 && !defined __CYGWIN__
typedef rb_data_type_t rb_random_data_type_t;
# define RB_RANDOM_PARENT 0
@@ -189,7 +244,7 @@ typedef const rb_data_type_t rb_random_data_type_t;
* 0, RB_RANDOM_INTERFACE_DEFINE(your),
* };
*
- * static inline constexpr your_prng = {
+ * static inline constexpr rb_random_data_type_t your_prng_type = {
* "your PRNG",
* { rb_random_mark, },
* RB_RANDOM_PARENT, // <<-- HERE
diff --git a/include/ruby/st.h b/include/ruby/st.h
index 1e4bb80686..f35ab43603 100644
--- a/include/ruby/st.h
+++ b/include/ruby/st.h
@@ -98,6 +98,8 @@ struct st_table {
enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE};
+size_t rb_st_table_size(const struct st_table *tbl);
+#define st_table_size rb_st_table_size
st_table *rb_st_init_table(const struct st_hash_type *);
#define st_init_table rb_st_init_table
st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t);
diff --git a/include/ruby/util.h b/include/ruby/util.h
index f0ea874322..e8727a3200 100644
--- a/include/ruby/util.h
+++ b/include/ruby/util.h
@@ -19,7 +19,7 @@
# include <stddef.h> /* size_t */
#endif
-#if HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* ssize_t */
#endif
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index ea460a5e46..18de3a17d8 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -19,11 +19,6 @@ RUBY_SYMBOL_EXPORT_BEGIN
*/
/*
- * Definitions for NT port of Perl
- */
-
-
-/*
* Ok now we can include the normal include files.
*/
@@ -130,8 +125,15 @@ typedef unsigned int uintptr_t;
#define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */
typedef int clockid_t;
+#if defined(__MINGW32__)
+#undef CLOCK_PROCESS_CPUTIME_ID
+#undef CLOCK_THREAD_CPUTIME_ID
+#undef CLOCK_REALTIME_COARSE
+#endif
+#if defined(HAVE_CLOCK_GETTIME) && !defined(CLOCK_REALTIME)
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
+#endif
#undef utime
#undef lseek
@@ -195,7 +197,6 @@ struct stati128 {
long st_ctimensec;
};
-#define off_t __int64
#define stat stati128
#undef SIZEOF_STRUCT_STAT_ST_INO
#define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64)
@@ -303,7 +304,6 @@ extern DWORD rb_w32_osver(void);
extern int rb_w32_uchown(const char *, int, int);
extern int rb_w32_ulink(const char *, const char *);
extern ssize_t rb_w32_ureadlink(const char *, char *, size_t);
-extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t);
extern int rb_w32_usymlink(const char *src, const char *link);
extern int gettimeofday(struct timeval *, struct timezone *);
extern int clock_gettime(clockid_t, struct timespec *);
@@ -394,6 +394,7 @@ scalb(double a, long b)
#endif
#define S_IFLNK 0xa000
+#define S_IFSOCK 0xc000
/*
* define this so we can do inplace editing
@@ -401,9 +402,9 @@ scalb(double a, long b)
#define SUFFIX
-extern int rb_w32_ftruncate(int fd, off_t length);
-extern int rb_w32_truncate(const char *path, off_t length);
-extern int rb_w32_utruncate(const char *path, off_t length);
+extern int rb_w32_ftruncate(int fd, rb_off_t length);
+extern int rb_w32_truncate(const char *path, rb_off_t length);
+extern int rb_w32_utruncate(const char *path, rb_off_t length);
#undef HAVE_FTRUNCATE
#define HAVE_FTRUNCATE 1
@@ -722,7 +723,7 @@ int rb_w32_fclose(FILE*);
int rb_w32_pipe(int[2]);
ssize_t rb_w32_read(int, void *, size_t);
ssize_t rb_w32_write(int, const void *, size_t);
-off_t rb_w32_lseek(int, off_t, int);
+rb_off_t rb_w32_lseek(int, rb_off_t, int);
int rb_w32_uutime(const char *, const struct utimbuf *);
int rb_w32_uutimes(const char *, const struct timeval *);
int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */);
@@ -815,7 +816,7 @@ double rb_w32_pow(double x, double y);
#define MAP_ANON 0x1000
#define MAP_ANONYMOUS MAP_ANON
-extern void *rb_w32_mmap(void *, size_t, int, int, int, off_t);
+extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t);
extern int rb_w32_munmap(void *, size_t);
extern int rb_w32_mprotect(void *, size_t, int);
diff --git a/inits.c b/inits.c
index 22ba6d5a8c..e809b56cc9 100644
--- a/inits.c
+++ b/inits.c
@@ -20,6 +20,7 @@ static void Init_builtin_prelude(void);
void
rb_call_inits(void)
{
+ CALL(default_shapes);
CALL(Thread_Mutex);
#if USE_TRANSIENT_HEAP
CALL(TransientHeap);
@@ -77,6 +78,7 @@ rb_call_inits(void)
CALL(vm_stack_canary);
CALL(ast);
CALL(gc_stress);
+ CALL(shape);
// enable builtin loading
CALL(builtin);
@@ -97,6 +99,7 @@ rb_call_builtin_inits(void)
BUILTIN(warning);
BUILTIN(array);
BUILTIN(kernel);
+ BUILTIN(symbol);
BUILTIN(timev);
BUILTIN(thread_sync);
BUILTIN(yjit);
@@ -104,6 +107,7 @@ rb_call_builtin_inits(void)
BUILTIN(marshal);
#if USE_MJIT
BUILTIN(mjit);
+ BUILTIN(mjit_c);
#endif
Init_builtin_prelude();
}
diff --git a/insns.def b/insns.def
index 15c4734b8b..9f5ee7095a 100644
--- a/insns.def
+++ b/insns.def
@@ -253,6 +253,29 @@ setclassvariable
vm_setclassvariable(GET_ISEQ(), GET_CFP(), id, val, ic);
}
+DEFINE_INSN
+opt_getconstant_path
+(IC ic)
+()
+(VALUE val)
+// attr bool leaf = false; /* may autoload or raise */
+{
+ const ID *segments = ic->segments;
+ struct iseq_inline_constant_cache_entry *ice = ic->entry;
+ if (ice && vm_ic_hit_p(ice, GET_EP())) {
+ val = ice->value;
+
+ VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
+ } else {
+ ruby_vm_constant_cache_misses++;
+ val = vm_get_ev_const_chain(ec, segments);
+ vm_ic_track_const_chain(GET_CFP(), ic, segments);
+ // Because leaf=false, we need to undo the PC increment to get the address to this instruction
+ // INSN_ATTR(width) == 2
+ vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
+ }
+}
+
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
are searched in the current scope. Otherwise, get constant under klass
class or module.
@@ -674,7 +697,7 @@ defined
{
val = Qnil;
if (vm_defined(ec, GET_CFP(), op_type, obj, v)) {
- val = pushval;
+ val = pushval;
}
}
@@ -1039,46 +1062,6 @@ branchnil
/* for optimize */
/**********************************************************/
-/* push inline-cached value and go to dst if it is valid */
-DEFINE_INSN
-opt_getinlinecache
-(OFFSET dst, IC ic)
-()
-(VALUE val)
-{
- struct iseq_inline_constant_cache_entry *ice = ic->entry;
-
- // If there isn't an entry, then we're going to walk through the ISEQ
- // starting at this instruction until we get to the associated
- // opt_setinlinecache and associate this inline cache with every getconstant
- // listed in between. We're doing this here instead of when the instructions
- // are first compiled because it's possible to turn off inline caches and we
- // want this to work in either case.
- if (!ice) {
- vm_ic_compile(GET_CFP(), ic);
- }
-
- if (ice && vm_ic_hit_p(ice, GET_EP())) {
- val = ice->value;
- JUMP(dst);
- }
- else {
- ruby_vm_constant_cache_misses++;
- val = Qnil;
- }
-}
-
-/* set inline cache */
-DEFINE_INSN
-opt_setinlinecache
-(IC ic)
-(VALUE val)
-(VALUE val)
-// attr bool leaf = false;
-{
- vm_ic_update(GET_ISEQ(), ic, val, GET_EP());
-}
-
/* run iseq only once */
DEFINE_INSN
once
diff --git a/internal.h b/internal.h
index 0740ae99e5..b63af50616 100644
--- a/internal.h
+++ b/internal.h
@@ -25,6 +25,9 @@
/* Prevent compiler from reordering access */
#define ACCESS_ONCE(type,x) (*((volatile type *)&(x)))
+#define UNDEF_P RB_UNDEF_P
+#define NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P
+
#include "ruby/ruby.h"
/* Following macros were formerly defined in this header but moved to somewhere
@@ -48,9 +51,6 @@
#undef RHASH_TBL
#undef RHASH_EMPTY_P
-/* internal/object.h */
-#undef ROBJECT_IV_INDEX_TBL
-
/* internal/struct.h */
#undef RSTRUCT_LEN
#undef RSTRUCT_PTR
diff --git a/internal/array.h b/internal/array.h
index 17d91a800b..a0d16dec3f 100644
--- a/internal/array.h
+++ b/internal/array.h
@@ -35,6 +35,7 @@ void rb_ary_cancel_sharing(VALUE ary);
size_t rb_ary_size_as_embedded(VALUE ary);
void rb_ary_make_embedded(VALUE ary);
bool rb_ary_embeddable_p(VALUE ary);
+VALUE rb_ary_diff(VALUE ary1, VALUE ary2);
static inline VALUE rb_ary_entry_internal(VALUE ary, long offset);
static inline bool ARY_PTR_USING_P(VALUE ary);
diff --git a/internal/basic_operators.h b/internal/basic_operators.h
new file mode 100644
index 0000000000..2cd9f50073
--- /dev/null
+++ b/internal/basic_operators.h
@@ -0,0 +1,64 @@
+#ifndef INTERNAL_BOP_H /*-*-C-*-vi:se ft=c:*/
+#define INTERNAL_BOP_H
+
+#include "internal.h"
+#include "ruby/internal/dllexport.h"
+
+enum ruby_basic_operators {
+ BOP_PLUS,
+ BOP_MINUS,
+ BOP_MULT,
+ BOP_DIV,
+ BOP_MOD,
+ BOP_EQ,
+ BOP_EQQ,
+ BOP_LT,
+ BOP_LE,
+ BOP_LTLT,
+ BOP_AREF,
+ BOP_ASET,
+ BOP_LENGTH,
+ BOP_SIZE,
+ BOP_EMPTY_P,
+ BOP_NIL_P,
+ BOP_SUCC,
+ BOP_GT,
+ BOP_GE,
+ BOP_NOT,
+ BOP_NEQ,
+ BOP_MATCH,
+ BOP_FREEZE,
+ BOP_UMINUS,
+ BOP_MAX,
+ BOP_MIN,
+ BOP_CALL,
+ BOP_AND,
+ BOP_OR,
+ BOP_CMP,
+ BOP_DEFAULT,
+
+ BOP_LAST_
+};
+
+MJIT_SYMBOL_EXPORT_BEGIN
+RUBY_EXTERN short ruby_vm_redefined_flag[BOP_LAST_];
+MJIT_SYMBOL_EXPORT_END
+
+/* optimize insn */
+#define INTEGER_REDEFINED_OP_FLAG (1 << 0)
+#define FLOAT_REDEFINED_OP_FLAG (1 << 1)
+#define STRING_REDEFINED_OP_FLAG (1 << 2)
+#define ARRAY_REDEFINED_OP_FLAG (1 << 3)
+#define HASH_REDEFINED_OP_FLAG (1 << 4)
+/* #define BIGNUM_REDEFINED_OP_FLAG (1 << 5) */
+#define SYMBOL_REDEFINED_OP_FLAG (1 << 6)
+#define TIME_REDEFINED_OP_FLAG (1 << 7)
+#define REGEXP_REDEFINED_OP_FLAG (1 << 8)
+#define NIL_REDEFINED_OP_FLAG (1 << 9)
+#define TRUE_REDEFINED_OP_FLAG (1 << 10)
+#define FALSE_REDEFINED_OP_FLAG (1 << 11)
+#define PROC_REDEFINED_OP_FLAG (1 << 12)
+
+#define BASIC_OP_UNREDEFINED_P(op, klass) (LIKELY((ruby_vm_redefined_flag[(op)]&(klass)) == 0))
+
+#endif
diff --git a/internal/class.h b/internal/class.h
index ae680564a6..63917e867f 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -14,6 +14,10 @@
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/intern.h" /* for rb_alloc_func_t */
#include "ruby/ruby.h" /* for struct RBasic */
+#include "shape.h"
+#include "ruby_assert.h"
+#include "vm_core.h"
+#include "method.h" /* for rb_cref_t */
#ifdef RCLASS_SUPER
# undef RCLASS_SUPER
@@ -25,24 +29,15 @@ struct rb_subclass_entry {
struct rb_subclass_entry *prev;
};
-struct rb_iv_index_tbl_entry {
- uint32_t index;
- rb_serial_t class_serial;
- VALUE class_value;
-};
-
struct rb_cvar_class_tbl_entry {
uint32_t index;
rb_serial_t global_cvar_state;
+ const rb_cref_t * cref;
VALUE class_value;
};
struct rb_classext_struct {
- struct st_table *iv_index_tbl; // ID -> struct rb_iv_index_tbl_entry
- struct st_table *iv_tbl;
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE /* otherwise m_tbl is in struct RClass */
- struct rb_id_table *m_tbl;
-#endif
+ VALUE *iv_ptr;
struct rb_id_table *const_tbl;
struct rb_id_table *callable_m_tbl;
struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */
@@ -57,63 +52,42 @@ struct rb_classext_struct {
* included. Hopefully that makes sense.
*/
struct rb_subclass_entry *module_subclass_entry;
-#if SIZEOF_SERIAL_T != SIZEOF_VALUE && !USE_RVARGC /* otherwise class_serial is in struct RClass */
- rb_serial_t class_serial;
-#endif
const VALUE origin_;
const VALUE refined_class;
rb_alloc_func_t allocator;
const VALUE includer;
+ uint32_t max_iv_count;
+ uint32_t variation_count;
+#if !SHAPE_IN_BASIC_FLAGS
+ shape_id_t shape_id;
+#endif
};
struct RClass {
struct RBasic basic;
VALUE super;
-#if !USE_RVARGC
- struct rb_classext_struct *ptr;
-#endif
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
- /* Class serial is as wide as VALUE. Place it here. */
- rb_serial_t class_serial;
-#else
- /* Class serial does not fit into struct RClass. Place m_tbl instead. */
struct rb_id_table *m_tbl;
-# if USE_RVARGC
- rb_serial_t *class_serial_ptr;
-# endif
+#if SIZE_POOL_COUNT == 1
+ struct rb_classext_struct *ptr;
#endif
};
typedef struct rb_subclass_entry rb_subclass_entry_t;
typedef struct rb_classext_struct rb_classext_t;
-#if USE_RVARGC
+#if RCLASS_EXT_EMBEDDED
# define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass)))
#else
# define RCLASS_EXT(c) (RCLASS(c)->ptr)
#endif
-#define RCLASS_IV_TBL(c) (RCLASS_EXT(c)->iv_tbl)
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
-# define RCLASS_M_TBL(c) (RCLASS_EXT(c)->m_tbl)
-#else
-# define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
-#endif
+#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
+#define RCLASS_IVPTR(c) (RCLASS_EXT(c)->iv_ptr)
#define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl)
#define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl)
#define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl)
-#define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl)
#define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_)
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
-#if SIZEOF_SERIAL_T == SIZEOF_VALUE
-# define RCLASS_SERIAL(c) (RCLASS(c)->class_serial)
-#else
-# if USE_RVARGC
-# define RCLASS_SERIAL(c) (*RCLASS(c)->class_serial_ptr)
-# else
-# define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial)
-# endif
-#endif
#define RCLASS_INCLUDER(c) (RCLASS_EXT(c)->includer)
#define RCLASS_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->subclass_entry)
#define RCLASS_MODULE_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->module_subclass_entry)
@@ -145,6 +119,7 @@ void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
void rb_class_detach_subclasses(VALUE);
void rb_class_detach_module_subclasses(VALUE);
void rb_class_remove_from_module_subclasses(VALUE);
+VALUE rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super);
VALUE rb_obj_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_protected_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_private_methods(int argc, const VALUE *argv, VALUE obj);
diff --git a/internal/cmdlineopt.h b/internal/cmdlineopt.h
index 71568a8745..bf52f1214b 100644
--- a/internal/cmdlineopt.h
+++ b/internal/cmdlineopt.h
@@ -36,6 +36,9 @@ typedef struct ruby_cmdline_options {
unsigned int do_split: 1;
unsigned int do_search: 1;
unsigned int setids: 2;
+#if USE_YJIT
+ unsigned int yjit: 1;
+#endif
} ruby_cmdline_options_t;
struct ruby_opt_message {
diff --git a/internal/compar.h b/internal/compar.h
index 5e336adafa..9115e4bd63 100644
--- a/internal/compar.h
+++ b/internal/compar.h
@@ -8,38 +8,18 @@
* file COPYING are met. Consult the file for details.
* @brief Internal header for Comparable.
*/
-#include "internal/vm.h" /* for rb_method_basic_definition_p */
+#include "internal/basic_operators.h"
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
-enum {
- cmp_opt_Integer,
- cmp_opt_String,
- cmp_opt_Float,
- cmp_optimizable_count
-};
+#define CMP_OPTIMIZABLE(type) BASIC_OP_UNREDEFINED_P(BOP_CMP, type##_REDEFINED_OP_FLAG)
-struct cmp_opt_data {
- unsigned int opt_methods;
- unsigned int opt_inited;
-};
-
-#define NEW_CMP_OPT_MEMO(type, value) \
- NEW_PARTIAL_MEMO_FOR(type, value, cmp_opt)
-#define CMP_OPTIMIZABLE_BIT(type) (1U << TOKEN_PASTE(cmp_opt_,type))
-#define CMP_OPTIMIZABLE(data, type) \
- (((data).opt_inited & CMP_OPTIMIZABLE_BIT(type)) ? \
- ((data).opt_methods & CMP_OPTIMIZABLE_BIT(type)) : \
- (((data).opt_inited |= CMP_OPTIMIZABLE_BIT(type)), \
- rb_method_basic_definition_p(TOKEN_PASTE(rb_c,type), id_cmp) && \
- ((data).opt_methods |= CMP_OPTIMIZABLE_BIT(type))))
-
-#define OPTIMIZED_CMP(a, b, data) \
- ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data, Integer)) ? \
+#define OPTIMIZED_CMP(a, b) \
+ ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(INTEGER)) ? \
(((long)a > (long)b) ? 1 : ((long)a < (long)b) ? -1 : 0) : \
- (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data, String)) ? \
+ (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(STRING)) ? \
rb_str_cmp(a, b) : \
- (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b) && CMP_OPTIMIZABLE(data, Float)) ? \
+ (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b) && CMP_OPTIMIZABLE(FLOAT)) ? \
rb_float_cmp(a, b) : \
rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b))
diff --git a/internal/cont.h b/internal/cont.h
index abffc97104..c3b091668a 100644
--- a/internal/cont.h
+++ b/internal/cont.h
@@ -9,6 +9,7 @@
* @brief Internal header for Fiber.
*/
#include "ruby/ruby.h" /* for VALUE */
+#include "iseq.h"
struct rb_thread_struct; /* in vm_core.h */
struct rb_fiber_struct; /* in cont.c */
@@ -17,7 +18,12 @@ struct rb_execution_context_struct; /* in vm_core.c */
/* cont.c */
void rb_fiber_reset_root_local_storage(struct rb_thread_struct *);
void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(VALUE), VALUE (*rollback_func)(VALUE));
-void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber);
+void rb_jit_cont_init(void);
+void rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data);
+void rb_jit_cont_finish(void);
+
+// Copy locals from the current execution to the specified fiber.
+VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber);
VALUE rb_fiberptr_self(struct rb_fiber_struct *fiber);
unsigned int rb_fiberptr_blocking(struct rb_fiber_struct *fiber);
diff --git a/internal/encoding.h b/internal/encoding.h
index c48cb24b04..a3b81bd388 100644
--- a/internal/encoding.h
+++ b/internal/encoding.h
@@ -12,6 +12,9 @@
#include "ruby/encoding.h" /* for rb_encoding */
#define rb_enc_autoload_p(enc) (!rb_enc_mbmaxlen(enc))
+#define rb_is_usascii_enc(enc) ((enc) == rb_usascii_encoding())
+#define rb_is_ascii8bit_enc(enc) ((enc) == rb_ascii8bit_encoding())
+#define rb_is_locale_enc(enc) ((enc) == rb_locale_encoding())
/* encoding.c */
ID rb_id_encoding(void);
@@ -24,7 +27,6 @@ int rb_encdb_dummy(const char *name);
void rb_encdb_declare(const char *name);
void rb_enc_set_base(const char *name, const char *orig);
int rb_enc_set_dummy(int index);
-void rb_encdb_set_unicode(int index);
PUREFUNC(int rb_data_is_encoding(VALUE obj));
#endif /* INTERNAL_ENCODING_H */
diff --git a/internal/eval.h b/internal/eval.h
index 73bb656d96..e594d8516d 100644
--- a/internal/eval.h
+++ b/internal/eval.h
@@ -21,6 +21,7 @@ extern ID ruby_static_id_status;
VALUE rb_refinement_module_get_refined_class(VALUE module);
void rb_class_modify_check(VALUE);
NORETURN(VALUE rb_f_raise(int argc, VALUE *argv));
+VALUE rb_top_main_class(const char *method);
/* eval_error.c */
VALUE rb_get_backtrace(VALUE info);
diff --git a/internal/gc.h b/internal/gc.h
index 84b7f9fa3e..e54a5dce9d 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -67,12 +67,16 @@ struct rb_objspace; /* in vm_core.h */
rb_obj_write((VALUE)(a), UNALIGNED_MEMBER_ACCESS((VALUE *)(slot)), \
(VALUE)(b), __FILE__, __LINE__)
-#if USE_RVARGC
+// We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools
+// The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID
+#if USE_RVARGC && (SIZEOF_UINT64_T == SIZEOF_VALUE)
# define SIZE_POOL_COUNT 5
#else
# define SIZE_POOL_COUNT 1
#endif
+#define RCLASS_EXT_EMBEDDED (SIZE_POOL_COUNT > 1)
+
typedef struct ractor_newobj_size_pool_cache {
struct RVALUE *freelist;
struct heap_page *using_page;
diff --git a/internal/hash.h b/internal/hash.h
index 657e5eff3c..64832c9610 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -73,6 +73,7 @@ VALUE rb_hash_default_value(VALUE hash, VALUE key);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
long rb_dbl_long_hash(double d);
st_table *rb_init_identtable(void);
+st_index_t rb_any_hash(VALUE a);
VALUE rb_to_hash_type(VALUE obj);
VALUE rb_hash_key_str(VALUE);
VALUE rb_hash_values(VALUE hash);
diff --git a/internal/numeric.h b/internal/numeric.h
index 19069cb3bc..89bc54b307 100644
--- a/internal/numeric.h
+++ b/internal/numeric.h
@@ -35,12 +35,16 @@ enum ruby_num_rounding_mode {
RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT,
};
+/* same as internal.h */
+#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
+#define roomof(x, y) (((x) + (y) - 1) / (y))
+#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
+
#if SIZEOF_DOUBLE <= SIZEOF_VALUE
typedef double rb_float_value_type;
#else
typedef struct {
- VALUE values[(SIZEOF_DOUBLE + SIZEOF_VALUE - 1) / SIZEOF_VALUE];
- /* roomof() needs internal.h, and the order of some macros may matter */
+ VALUE values[roomof(SIZEOF_DOUBLE, SIZEOF_VALUE)];
} rb_float_value_type;
#endif
diff --git a/internal/object.h b/internal/object.h
index 88f3a44bc6..7b54e13dd2 100644
--- a/internal/object.h
+++ b/internal/object.h
@@ -9,11 +9,6 @@
* @brief Internal header for Object.
*/
#include "ruby/ruby.h" /* for VALUE */
-#include "internal/class.h" /* for RCLASS_IV_INDEX_TBL */
-
-#ifdef ROBJECT_IV_INDEX_TBL
-# undef ROBJECT_IV_INDEX_TBL
-#endif
/* object.c */
VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
@@ -26,7 +21,6 @@ int rb_bool_expected(VALUE, const char *, int raise);
static inline void RBASIC_CLEAR_CLASS(VALUE obj);
static inline void RBASIC_SET_CLASS_RAW(VALUE obj, VALUE klass);
static inline void RBASIC_SET_CLASS(VALUE obj, VALUE klass);
-static inline struct st_table *ROBJECT_IV_INDEX_TBL_inline(VALUE obj);
RUBY_SYMBOL_EXPORT_BEGIN
/* object.c (export) */
@@ -64,20 +58,4 @@ RBASIC_SET_CLASS(VALUE obj, VALUE klass)
RBASIC_SET_CLASS_RAW(obj, klass);
RB_OBJ_WRITTEN(obj, oldv, klass);
}
-
-RBIMPL_ATTR_PURE()
-static inline struct st_table *
-ROBJECT_IV_INDEX_TBL_inline(VALUE obj)
-{
- if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
- VALUE klass = rb_obj_class(obj);
- return RCLASS_IV_INDEX_TBL(klass);
- }
- else {
- const struct RObject *const ptr = ROBJECT(obj);
- return ptr->as.heap.iv_index_tbl;
- }
-}
-#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL_inline
-
#endif /* INTERNAL_OBJECT_H */
diff --git a/internal/parse.h b/internal/parse.h
index d9f5b56bc5..f242c384ad 100644
--- a/internal/parse.h
+++ b/internal/parse.h
@@ -15,6 +15,8 @@ struct rb_iseq_struct; /* in vm_core.h */
VALUE rb_parser_set_yydebug(VALUE, VALUE);
void *rb_parser_load_file(VALUE parser, VALUE name);
void rb_parser_keep_script_lines(VALUE vparser);
+void rb_parser_error_tolerant(VALUE vparser);
+void rb_parser_keep_tokens(VALUE vparser);
RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
diff --git a/internal/string.h b/internal/string.h
index 8fb9553d03..12edbff2b1 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -43,6 +43,8 @@ char *rb_str_to_cstr(VALUE str);
const char *ruby_escaped_char(int c);
void rb_str_make_independent(VALUE str);
int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc);
+int rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code);
+VALUE rb_str_include(VALUE str, VALUE arg);
static inline bool STR_EMBED_P(VALUE str);
static inline bool STR_SHARED_P(VALUE str);
@@ -104,7 +106,7 @@ STR_EMBED_P(VALUE str)
static inline bool
STR_SHARED_P(VALUE str)
{
- return FL_ALL_RAW(str, STR_NOEMBED | ELTS_SHARED);
+ return FL_ALL_RAW(str, STR_NOEMBED | STR_SHARED);
}
static inline bool
diff --git a/internal/thread.h b/internal/thread.h
index 63f03bb94a..c3e54de683 100644
--- a/internal/thread.h
+++ b/internal/thread.h
@@ -20,6 +20,7 @@ struct rb_thread_struct; /* in vm_core.h */
#define COVERAGE_TARGET_BRANCHES 2
#define COVERAGE_TARGET_METHODS 4
#define COVERAGE_TARGET_ONESHOT_LINES 8
+#define COVERAGE_TARGET_EVAL 16
VALUE rb_obj_is_mutex(VALUE obj);
VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg);
@@ -28,6 +29,7 @@ VALUE rb_get_coverages(void);
int rb_get_coverage_mode(void);
VALUE rb_default_coverage(int);
VALUE rb_thread_shield_new(void);
+bool rb_thread_shield_owned(VALUE self);
VALUE rb_thread_shield_wait(VALUE self);
VALUE rb_thread_shield_release(VALUE self);
VALUE rb_thread_shield_destroy(VALUE self);
diff --git a/internal/time.h b/internal/time.h
index a3bf0587ec..e21b3574f6 100644
--- a/internal/time.h
+++ b/internal/time.h
@@ -28,7 +28,10 @@ struct timeval rb_time_timeval(VALUE);
RUBY_SYMBOL_EXPORT_BEGIN
/* time.c (export) */
void ruby_reset_leap_second_info(void);
-void ruby_reset_timezone(void);
+#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY
+RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY()
+#endif
+void ruby_reset_timezone(const char *);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_TIME_H */
diff --git a/internal/variable.h b/internal/variable.h
index 1a19e8964b..6dec6a6759 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -13,6 +13,7 @@
#include "constant.h" /* for rb_const_entry_t */
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for VALUE */
+#include "shape.h" /* for rb_shape_t */
/* global variable */
@@ -24,7 +25,6 @@ void rb_gc_update_global_tbl(void);
size_t rb_generic_ivar_memsize(VALUE);
VALUE rb_search_class_path(VALUE);
VALUE rb_attr_delete(VALUE, ID);
-VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef);
void rb_autoload_str(VALUE mod, ID id, VALUE file);
VALUE rb_autoload_at_p(VALUE, ID, int);
NORETURN(VALUE rb_mod_const_missing(VALUE,VALUE));
@@ -35,7 +35,10 @@ void rb_gvar_ractor_local(const char *name);
static inline bool ROBJ_TRANSIENT_P(VALUE obj);
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
static inline void ROBJ_TRANSIENT_UNSET(VALUE obj);
-uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id);
+
+struct gen_ivtbl;
+int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl);
+int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg);
RUBY_SYMBOL_EXPORT_BEGIN
/* variable.c (export) */
@@ -47,11 +50,15 @@ void rb_iv_tbl_copy(VALUE dst, VALUE src);
RUBY_SYMBOL_EXPORT_END
MJIT_SYMBOL_EXPORT_BEGIN
+VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef);
VALUE rb_gvar_get(ID);
VALUE rb_gvar_set(ID, VALUE);
VALUE rb_gvar_defined(ID);
void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID);
-void rb_init_iv_list(VALUE obj);
+rb_shape_t * rb_grow_iv_list(VALUE obj);
+void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize);
+struct gen_ivtbl *rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize);
+attr_index_t rb_obj_ivar_set(VALUE obj, ID id, VALUE val);
MJIT_SYMBOL_EXPORT_END
static inline bool
diff --git a/internal/vm.h b/internal/vm.h
index c6c6b2ccc2..cf245c6579 100644
--- a/internal/vm.h
+++ b/internal/vm.h
@@ -40,7 +40,7 @@ enum method_missing_reason {
};
/* vm_insnhelper.h */
-rb_serial_t rb_next_class_serial(void);
+VALUE rb_vm_push_frame_fname(struct rb_execution_context_struct *ec, VALUE fname);
/* vm.c */
VALUE rb_obj_is_thread(VALUE obj);
diff --git a/io.c b/io.c
index f2c64989df..99ec59da29 100644
--- a/io.c
+++ b/io.c
@@ -75,10 +75,6 @@
#include <sys/fcntl.h>
#endif
-#if !HAVE_OFF_T && !defined(off_t)
-# define off_t long
-#endif
-
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
@@ -149,10 +145,6 @@
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
-#if SIZEOF_OFF_T > SIZEOF_LONG && !defined(HAVE_LONG_LONG)
-# error off_t is bigger than long, but you have no long long...
-#endif
-
#ifndef PIPE_BUF
# ifdef _POSIX_PIPE_BUF
# define PIPE_BUF _POSIX_PIPE_BUF
@@ -175,6 +167,8 @@ off_t __syscall(quad_t number, ...);
#define IO_RBUF_CAPA_FOR(fptr) (NEED_READCONV(fptr) ? IO_CBUF_CAPA_MIN : IO_RBUF_CAPA_MIN)
#define IO_WBUF_CAPA_MIN 8192
+#define IO_MAX_BUFFER_GROWTH 8 * 1024 * 1024 // 8MB
+
/* define system APIs */
#ifdef _WIN32
#undef open
@@ -186,6 +180,7 @@ off_t __syscall(quad_t number, ...);
VALUE rb_cIO;
VALUE rb_eEOFError;
VALUE rb_eIOError;
+VALUE rb_eIOTimeoutError;
VALUE rb_mWaitReadable;
VALUE rb_mWaitWritable;
@@ -501,7 +496,7 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
#if defined(_WIN32)
#define WAIT_FD_IN_WIN32(fptr) \
- (rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil))
+ (rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT))
#else
#define WAIT_FD_IN_WIN32(fptr)
#endif
@@ -620,7 +615,7 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo)
static void
io_unread(rb_io_t *fptr)
{
- off_t r, pos;
+ rb_off_t r, pos;
ssize_t read_size;
long i;
long newlines = 0;
@@ -839,6 +834,54 @@ rb_io_set_write_io(VALUE io, VALUE w)
/*
* call-seq:
+ * timeout -> duration or nil
+ *
+ * Get the internal timeout duration or nil if it was not set.
+ *
+ */
+VALUE
+rb_io_timeout(VALUE self)
+{
+ rb_io_t *fptr = rb_io_get_fptr(self);
+
+ return fptr->timeout;
+}
+
+/*
+ * call-seq:
+ * timeout = duration -> duration
+ * timeout = nil -> nil
+ *
+ * Set the internal timeout to the specified duration or nil. The timeout
+ * applies to all blocking operations where possible.
+ *
+ * This affects the following methods (but is not limited to): #gets, #puts,
+ * #read, #write, #wait_readable and #wait_writable. This also affects
+ * blocking socket operations like Socket#accept and Socket#connect.
+ *
+ * Some operations like File#open and IO#close are not affected by the
+ * timeout. A timeout during a write operation may leave the IO in an
+ * inconsistent state, e.g. data was partially written. Generally speaking, a
+ * timeout is a last ditch effort to prevent an application from hanging on
+ * slow I/O operations, such as those that occur during a slowloris attack.
+ */
+VALUE
+rb_io_set_timeout(VALUE self, VALUE timeout)
+{
+ // Validate it:
+ if (RTEST(timeout)) {
+ rb_time_interval(timeout);
+ }
+
+ rb_io_t *fptr = rb_io_get_fptr(self);
+
+ fptr->timeout = timeout;
+
+ return self;
+}
+
+/*
+ * call-seq:
* IO.try_convert(object) -> new_io or nil
*
* Attempts to convert +object+ into an \IO object via method +to_io+;
@@ -859,7 +902,7 @@ rb_io_s_try_convert(VALUE dummy, VALUE io)
static void
io_unread(rb_io_t *fptr)
{
- off_t r;
+ rb_off_t r;
rb_io_check_closed(fptr);
if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX)
return;
@@ -1008,7 +1051,7 @@ void
rb_io_read_check(rb_io_t *fptr)
{
if (!READ_DATA_PENDING(fptr)) {
- rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
+ rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT);
}
return;
}
@@ -1060,56 +1103,124 @@ struct io_internal_read_struct {
VALUE th;
rb_io_t *fptr;
int nonblock;
+ int fd;
+
void *buf;
size_t capa;
+ struct timeval *timeout;
};
struct io_internal_write_struct {
+ VALUE th;
+ rb_io_t *fptr;
+ int nonblock;
int fd;
+
const void *buf;
size_t capa;
+ struct timeval *timeout;
};
#ifdef HAVE_WRITEV
struct io_internal_writev_struct {
+ VALUE th;
+ rb_io_t *fptr;
+ int nonblock;
int fd;
+
int iovcnt;
const struct iovec *iov;
+ struct timeval *timeout;
};
#endif
-static int nogvl_wait_for(VALUE th, rb_io_t *fptr, short events);
+static int nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout);
+
+/**
+ * Wait for the given events on the given file descriptor.
+ * Returns -1 if an error or timeout occurred. +errno+ will be set.
+ * Returns the event mask if an event occurred.
+ */
+static inline int
+io_internal_wait(VALUE thread, rb_io_t *fptr, int error, int events, struct timeval *timeout)
+{
+ int ready = nogvl_wait_for(thread, fptr, events, timeout);
+
+ if (ready > 0) {
+ return ready;
+ }
+ else if (ready == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ errno = error;
+ return -1;
+}
+
static VALUE
internal_read_func(void *ptr)
{
struct io_internal_read_struct *iis = ptr;
- ssize_t r;
-retry:
- r = read(iis->fptr->fd, iis->buf, iis->capa);
- if (r < 0 && !iis->nonblock) {
- int e = errno;
- if (io_again_p(e)) {
- if (nogvl_wait_for(iis->th, iis->fptr, RB_WAITFD_IN) != -1) {
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_IN, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
+ result = read(iis->fd, iis->buf, iis->capa);
+
+ if (result < 0 && !iis->nonblock) {
+ if (io_again_p(errno)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_IN, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
goto retry;
}
- errno = e;
}
}
- return r;
+
+ return result;
}
#if defined __APPLE__
-# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE)
+# define do_write_retry(code) do {result = code;} while (result == -1 && errno == EPROTOTYPE)
#else
-# define do_write_retry(code) ret = code
+# define do_write_retry(code) result = code
#endif
+
static VALUE
internal_write_func(void *ptr)
{
struct io_internal_write_struct *iis = ptr;
- ssize_t ret;
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
do_write_retry(write(iis->fd, iis->buf, iis->capa));
- return (VALUE)ret;
+
+ if (result < 0 && !iis->nonblock) {
+ int e = errno;
+ if (io_again_p(e)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
+ goto retry;
+ }
+ }
+ }
+
+ return result;
}
#ifdef HAVE_WRITEV
@@ -1117,20 +1228,40 @@ static VALUE
internal_writev_func(void *ptr)
{
struct io_internal_writev_struct *iis = ptr;
- ssize_t ret;
+ ssize_t result;
+
+ if (iis->timeout && !iis->nonblock) {
+ if (io_internal_wait(iis->th, iis->fptr, 0, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ }
+
+ retry:
do_write_retry(writev(iis->fd, iis->iov, iis->iovcnt));
- return (VALUE)ret;
+
+ if (result < 0 && !iis->nonblock) {
+ if (io_again_p(errno)) {
+ if (io_internal_wait(iis->th, iis->fptr, errno, RB_WAITFD_OUT, iis->timeout) == -1) {
+ return -1;
+ }
+ else {
+ goto retry;
+ }
+ }
+ }
+
+ return result;
}
#endif
static ssize_t
-rb_read_internal(rb_io_t *fptr, void *buf, size_t count)
+rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, fptr->self, buf, count, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return rb_fiber_scheduler_io_result_apply(result);
}
}
@@ -1139,31 +1270,53 @@ rb_read_internal(rb_io_t *fptr, void *buf, size_t count)
.th = rb_thread_current(),
.fptr = fptr,
.nonblock = 0,
+ .fd = fptr->fd,
+
.buf = buf,
- .capa = count
+ .capa = count,
+ .timeout = NULL,
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_read_func, &iis, fptr->fd);
}
static ssize_t
-rb_write_internal(rb_io_t *fptr, const void *buf, size_t count)
+rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, buf, count, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return rb_fiber_scheduler_io_result_apply(result);
}
}
struct io_internal_write_struct iis = {
+ .th = rb_thread_current(),
+ .fptr = fptr,
+ .nonblock = 0,
.fd = fptr->fd,
+
.buf = buf,
- .capa = count
+ .capa = count,
+ .timeout = NULL
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_write_func, &iis, fptr->fd);
}
@@ -1171,23 +1324,36 @@ rb_write_internal(rb_io_t *fptr, const void *buf, size_t count)
static ssize_t
rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt)
{
+ if (!iovcnt) return 0;
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- for (int i = 0; i < iovcnt; i += 1) {
- VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[i].iov_base, iov[i].iov_len, 0);
+ // This path assumes at least one `iov`:
+ VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[0].iov_base, iov[0].iov_len, 0);
- if (result != Qundef) {
- return rb_fiber_scheduler_io_result_apply(result);
- }
+ if (!UNDEF_P(result)) {
+ return rb_fiber_scheduler_io_result_apply(result);
}
}
struct io_internal_writev_struct iis = {
+ .th = rb_thread_current(),
+ .fptr = fptr,
+ .nonblock = 0,
.fd = fptr->fd,
+
.iov = iov,
.iovcnt = iovcnt,
+ .timeout = NULL
};
+ struct timeval timeout_storage;
+
+ if (fptr->timeout != Qnil) {
+ timeout_storage = rb_time_interval(fptr->timeout);
+ iis.timeout = &timeout_storage;
+ }
+
return (ssize_t)rb_thread_io_blocking_region(internal_writev_func, &iis, fptr->fd);
}
#endif
@@ -1204,11 +1370,13 @@ io_flush_buffer_sync(void *arg)
fptr->wbuf.len = 0;
return 0;
}
+
if (0 <= r) {
fptr->wbuf.off += (int)r;
fptr->wbuf.len -= (int)r;
errno = EAGAIN;
}
+
return (VALUE)-1;
}
@@ -1239,7 +1407,7 @@ io_fflush(rb_io_t *fptr)
return 0;
while (fptr->wbuf.len > 0 && io_flush_buffer(fptr) != 0) {
- if (!rb_io_maybe_wait_writable(errno, fptr->self, Qnil))
+ if (!rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT))
return -1;
rb_io_check_closed(fptr);
@@ -1263,6 +1431,10 @@ rb_io_wait(VALUE io, VALUE events, VALUE timeout)
struct timeval tv_storage;
struct timeval *tv = NULL;
+ if (NIL_OR_UNDEF_P(timeout)) {
+ timeout = fptr->timeout;
+ }
+
if (timeout != Qnil) {
tv_storage = rb_time_interval(timeout);
tv = &tv_storage;
@@ -1481,7 +1653,7 @@ make_writeconv(rb_io_t *fptr)
ecflags = fptr->encs.ecflags & ~ECONV_NEWLINE_DECORATOR_READ_MASK;
ecopts = fptr->encs.ecopts;
- if (!fptr->encs.enc || (fptr->encs.enc == rb_ascii8bit_encoding() && !fptr->encs.enc2)) {
+ if (!fptr->encs.enc || (rb_is_ascii8bit_enc(fptr->encs.enc) && !fptr->encs.enc2)) {
/* no encoding conversion */
fptr->writeconv_pre_ecflags = 0;
fptr->writeconv_pre_ecopts = Qnil;
@@ -1570,7 +1742,7 @@ io_binwrite_string_internal(rb_io_t *fptr, const char *ptr, long length)
return result;
}
else {
- return rb_write_internal(fptr, ptr, length);
+ return rb_io_write_memory(fptr, ptr, length);
}
}
#else
@@ -1605,7 +1777,7 @@ io_binwrite_string_internal(rb_io_t *fptr, const char *ptr, long length)
}
// Otherwise, we should write the data directly:
- return rb_write_internal(fptr, ptr, length);
+ return rb_io_write_memory(fptr, ptr, length);
}
#endif
@@ -1621,19 +1793,17 @@ io_binwrite_string(VALUE arg)
// Write as much as possible:
ssize_t result = io_binwrite_string_internal(p->fptr, ptr, remaining);
- // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
- // should try again.
if (result == 0) {
- errno = EWOULDBLOCK;
+ // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
+ // should try again immediately.
}
-
- if (result > 0) {
+ else if (result > 0) {
if ((size_t)result == remaining) break;
ptr += result;
remaining -= result;
}
// Wait for it to become writable:
- else if (rb_io_maybe_wait_writable(errno, p->fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, p->fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(p->fptr);
}
else {
@@ -1867,7 +2037,7 @@ io_binwritev_internal(VALUE arg)
while (remaining) {
long result = rb_writev_internal(fptr, iov, iovcnt);
- if (result > 0) {
+ if (result >= 0) {
offset += result;
if (fptr->wbuf.ptr && fptr->wbuf.len) {
if (offset < (size_t)fptr->wbuf.len) {
@@ -1900,7 +2070,7 @@ io_binwritev_internal(VALUE arg)
iov->iov_base = (char *)iov->iov_base + result;
iov->iov_len -= result;
}
- else if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
}
else {
@@ -2065,7 +2235,8 @@ io_writev(int argc, const VALUE *argv, VALUE io)
* write(*objects) -> integer
*
* Writes each of the given +objects+ to +self+,
- * which must be opened for writing (see IO@Modes);
+ * which must be opened for writing
+ * (see {Access Modes}[rdoc-ref:File@Access+Modes]);
* returns the total number bytes written;
* each of +objects+ that is not a string is converted via method +to_s+:
*
@@ -2077,6 +2248,7 @@ io_writev(int argc, const VALUE *argv, VALUE io)
* Hello, World!
* foobar2
*
+ * Related: IO#read.
*/
static VALUE
@@ -2124,7 +2296,7 @@ rb_io_writev(VALUE io, int argc, const VALUE *argv)
* self << object -> self
*
* Writes the given +object+ to +self+,
- * which must be opened for writing (see IO@Modes);
+ * which must be opened for writing (see {Access Modes}[rdoc-ref:File@Access+Modes]);
* returns +self+;
* if +object+ is not a string, it is converted via method +to_s+:
*
@@ -2224,7 +2396,7 @@ static VALUE
rb_io_tell(VALUE io)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
GetOpenFile(io, fptr);
pos = io_tell(fptr);
@@ -2237,7 +2409,7 @@ static VALUE
rb_io_seek(VALUE io, VALUE offset, int whence)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
@@ -2348,7 +2520,7 @@ static VALUE
rb_io_set_pos(VALUE io, VALUE offset)
{
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
@@ -2405,12 +2577,12 @@ rb_io_rewind(VALUE io)
static int
fptr_wait_readable(rb_io_t *fptr)
{
- int ret = rb_io_maybe_wait_readable(errno, fptr->self, Qnil);
+ int result = rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
- if (ret)
+ if (result)
rb_io_check_closed(fptr);
- return ret;
+ return result;
}
static int
@@ -2429,7 +2601,7 @@ io_fillbuf(rb_io_t *fptr)
}
if (fptr->rbuf.len == 0) {
retry:
- r = rb_read_internal(fptr, fptr->rbuf.ptr, fptr->rbuf.capa);
+ r = rb_io_read_memory(fptr, fptr->rbuf.ptr, fptr->rbuf.capa);
if (r < 0) {
if (fptr_wait_readable(fptr))
@@ -2466,7 +2638,7 @@ io_fillbuf(rb_io_t *fptr)
* f.close
*
* Raises an exception unless the stream is opened for reading;
- * see {Mode}[rdoc-ref:IO@Mode].
+ * see {Mode}[rdoc-ref:File@Access+Modes].
*
* If +self+ is a stream such as pipe or socket, this method
* blocks until the other end sends some data or closes it:
@@ -2739,6 +2911,29 @@ rb_io_pid(VALUE io)
return PIDT2NUM(fptr->pid);
}
+/*
+ * call-seq:
+ * path -> string or nil
+ *
+ * Returns the path associated with the IO, or +nil+ if there is no path
+ * associated with the IO. It is not guaranteed that the path exists on
+ * the filesystem.
+ *
+ * $stdin.path # => "<STDIN>"
+ *
+ * File.open("testfile") {|f| f.path} # => "testfile"
+ */
+
+static VALUE
+rb_io_path(VALUE io)
+{
+ rb_io_t *fptr = RFILE(io)->fptr;
+
+ if (!fptr)
+ return Qnil;
+
+ return rb_obj_dup(fptr->pathv);
+}
/*
* call-seq:
@@ -2821,7 +3016,7 @@ io_bufread(char *ptr, long len, rb_io_t *fptr)
while (n > 0) {
again:
rb_io_check_closed(fptr);
- c = rb_read_internal(fptr, ptr+offset, n);
+ c = rb_io_read_memory(fptr, ptr+offset, n);
if (c == 0) break;
if (c < 0) {
if (fptr_wait_readable(fptr))
@@ -2884,8 +3079,8 @@ static long
remain_size(rb_io_t *fptr)
{
struct stat st;
- off_t siz = READ_DATA_PENDING_COUNT(fptr);
- off_t pos;
+ rb_off_t siz = READ_DATA_PENDING_COUNT(fptr);
+ rb_off_t pos;
if (fstat(fptr->fd, &st) == 0 && S_ISREG(st.st_mode)
#if defined(__HAIKU__)
@@ -3061,7 +3256,8 @@ static int
io_setstrbuf(VALUE *str, long len)
{
#ifdef _WIN32
- len = (len + 1) & ~1L; /* round up for wide char */
+ if (len > 0)
+ len = (len + 1) & ~1L; /* round up for wide char */
#endif
if (NIL_P(*str)) {
*str = rb_str_new(0, len);
@@ -3076,7 +3272,9 @@ io_setstrbuf(VALUE *str, long len)
}
len -= clen;
}
- rb_str_modify_expand(*str, len);
+ if ((rb_str_capacity(*str) - (size_t)RSTRING_LEN(*str)) < (size_t)len) {
+ rb_str_modify_expand(*str, len);
+ }
return FALSE;
}
@@ -3159,7 +3357,17 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
pos += rb_str_coderange_scan_restartable(RSTRING_PTR(str) + pos, RSTRING_PTR(str) + bytes, enc, &cr);
if (bytes < siz) break;
siz += BUFSIZ;
- rb_str_modify_expand(str, BUFSIZ);
+
+ size_t capa = rb_str_capacity(str);
+ if (capa < (size_t)RSTRING_LEN(str) + BUFSIZ) {
+ if (capa < BUFSIZ) {
+ capa = BUFSIZ;
+ }
+ else if (capa > IO_MAX_BUFFER_GROWTH) {
+ capa = IO_MAX_BUFFER_GROWTH;
+ }
+ rb_str_modify_expand(str, capa);
+ }
}
if (shrinkable) io_shrink_read_string(str, RSTRING_LEN(str));
str = io_enc_str(str, fptr);
@@ -3176,7 +3384,7 @@ rb_io_set_nonblock(rb_io_t *fptr)
}
static VALUE
-read_internal_call(VALUE arg)
+io_read_memory_call(VALUE arg)
{
struct io_internal_read_struct *iis = (struct io_internal_read_struct *)arg;
@@ -3184,7 +3392,7 @@ read_internal_call(VALUE arg)
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, iis->fptr->self, iis->buf, iis->capa, 0);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
// This is actually returned as a pseudo-VALUE and later cast to a long:
return (VALUE)rb_fiber_scheduler_io_result_apply(result);
}
@@ -3194,9 +3402,9 @@ read_internal_call(VALUE arg)
}
static long
-read_internal_locktmp(VALUE str, struct io_internal_read_struct *iis)
+io_read_memory_locktmp(VALUE str, struct io_internal_read_struct *iis)
{
- return (long)rb_str_locktmp_ensure(str, read_internal_call, (VALUE)iis);
+ return (long)rb_str_locktmp_ensure(str, io_read_memory_call, (VALUE)iis);
}
#define no_exception_p(opts) !rb_opts_exception_p((opts), TRUE)
@@ -3238,9 +3446,11 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int no_exception, int nonblock)
iis.th = rb_thread_current();
iis.fptr = fptr;
iis.nonblock = nonblock;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = len;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
int e = errno;
if (!nonblock && fptr_wait_readable(fptr))
@@ -3401,13 +3611,15 @@ io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str,
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
if (n <= 0) {
- rb_io_set_nonblock(fptr);
+ rb_fd_set_nonblock(fptr->fd);
shrinkable |= io_setstrbuf(&str, len);
iis.fptr = fptr;
iis.nonblock = 1;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = len;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
int e = errno;
if (io_again_p(e)) {
@@ -3446,7 +3658,7 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
- rb_io_set_nonblock(fptr);
+ rb_fd_set_nonblock(fptr->fd);
n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
RB_GC_GUARD(str);
@@ -3468,14 +3680,13 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
/*
* call-seq:
- * read(maxlen = nil) -> string or nil
- * read(maxlen = nil, out_string) -> out_string or nil
+ * read(maxlen = nil, out_string = nil) -> new_string, out_string, or nil
*
- * Reads bytes from the stream (in binary mode):
+ * Reads bytes from the stream; the stream must be opened for reading
+ * (see {Access Modes}[rdoc-ref:File@Access+Modes]):
*
- * - If +maxlen+ is +nil+, reads all bytes.
- * - Otherwise reads +maxlen+ bytes, if available.
- * - Otherwise reads all bytes.
+ * - If +maxlen+ is +nil+, reads all bytes using the stream's data mode.
+ * - Otherwise reads up to +maxlen+ bytes in binary mode.
*
* Returns a string (either a new string or the given +out_string+)
* containing the bytes read.
@@ -3535,6 +3746,7 @@ io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex)
* If you need the behavior like a single read(2) system call,
* consider #readpartial, #read_nonblock, and #sysread.
*
+ * Related: IO#write.
*/
static VALUE
@@ -3593,8 +3805,33 @@ rscheck(const char *rsptr, long rslen, VALUE rs)
rb_raise(rb_eRuntimeError, "rs modified");
}
+static const char *
+search_delim(const char *p, long len, int delim, rb_encoding *enc)
+{
+ if (rb_enc_mbminlen(enc) == 1) {
+ p = memchr(p, delim, len);
+ if (p) return p + 1;
+ }
+ else {
+ const char *end = p + len;
+ while (p < end) {
+ int r = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ p += rb_enc_mbminlen(enc);
+ continue;
+ }
+ int n = MBCLEN_CHARFOUND_LEN(r);
+ if (rb_enc_mbc_to_codepoint(p, end, enc) == (unsigned int)delim) {
+ return p + n;
+ }
+ p += n;
+ }
+ }
+ return NULL;
+}
+
static int
-appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
+appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp, rb_encoding *enc)
{
VALUE str = *strp;
long limit = *lp;
@@ -3609,9 +3846,9 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
p = READ_CHAR_PENDING_PTR(fptr);
if (0 < limit && limit < searchlen)
searchlen = (int)limit;
- e = memchr(p, delim, searchlen);
+ e = search_delim(p, searchlen, delim, enc);
if (e) {
- int len = (int)(e-p+1);
+ int len = (int)(e-p);
if (NIL_P(str))
*strp = str = rb_str_new(p, len);
else
@@ -3651,8 +3888,8 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
long last;
if (limit > 0 && pending > limit) pending = limit;
- e = memchr(p, delim, pending);
- if (e) pending = e - p + 1;
+ e = search_delim(p, pending, delim, enc);
+ if (e) pending = e - p;
if (!NIL_P(str)) {
last = RSTRING_LEN(str);
rb_str_resize(str, last + pending);
@@ -3798,7 +4035,7 @@ extract_getline_opts(VALUE opts, struct getline_arg *args)
kwds[0] = rb_intern_const("chomp");
}
rb_get_kwargs(opts, kwds, 0, -2, &vchomp);
- chomp = (vchomp != Qundef) && RTEST(vchomp);
+ chomp = (!UNDEF_P(vchomp)) && RTEST(vchomp);
}
args->chomp = chomp;
}
@@ -3912,16 +4149,26 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
rsptr = RSTRING_PTR(rs);
rslen = RSTRING_LEN(rs);
}
+ newline = '\n';
+ }
+ else if (rb_enc_mbminlen(enc) == 1) {
+ rsptr = RSTRING_PTR(rs);
+ newline = (unsigned char)rsptr[rslen - 1];
}
else {
+ rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
rsptr = RSTRING_PTR(rs);
+ const char *e = rsptr + rslen;
+ const char *last = rb_enc_prev_char(rsptr, e, e, enc);
+ int n;
+ newline = rb_enc_codepoint_len(last, e, &n, enc);
+ if (last + n != e) rb_raise(rb_eArgError, "broken separator");
}
- newline = (unsigned char)rsptr[rslen - 1];
- chomp_cr = chomp && rslen == 1 && newline == '\n';
+ chomp_cr = chomp && newline == '\n' && rslen == rb_enc_mbminlen(enc);
}
/* MS - Optimization */
- while ((c = appendline(fptr, newline, &str, &limit)) != EOF) {
+ while ((c = appendline(fptr, newline, &str, &limit, enc)) != EOF) {
const char *s, *p, *pp, *e;
if (c == newline) {
@@ -3943,8 +4190,8 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
if (limit == 0) {
s = RSTRING_PTR(str);
p = RSTRING_END(str);
- pp = rb_enc_left_char_head(s, p-1, p, enc);
- if (extra_limit &&
+ pp = rb_enc_prev_char(s, p, p, enc);
+ if (extra_limit && pp &&
MBCLEN_NEEDMORE_P(rb_enc_precise_mbclen(pp, p, enc))) {
/* relax the limit while incomplete character.
* extra_limit limits the relax length */
@@ -4019,13 +4266,13 @@ rb_io_gets_internal(VALUE io)
/*
* call-seq:
- * gets(sep = $/, **line_opts) -> string or nil
- * gets(limit, **line_opts) -> string or nil
- * gets(sep, limit, **line_opts) -> string or nil
+ * gets(sep = $/, chomp: false) -> string or nil
+ * gets(limit, chomp: false) -> string or nil
+ * gets(sep, limit, chomp: false) -> string or nil
*
- * Reads and returns a line from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
+ * Reads and returns a line from the stream;
* assigns the return value to <tt>$_</tt>.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, returns the next line
* as determined by line separator <tt>$/</tt>, or +nil+ if none:
@@ -4063,7 +4310,7 @@ rb_io_gets_internal(VALUE io)
*
* With only integer argument +limit+ given,
* limits the number of bytes in the line;
- * see {Line Limit}}[rdoc-ref:IO@Line+Limit]:
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit]:
*
* # No more than one line.
* File.open('t.txt') {|f| f.gets(10) } # => "First line"
@@ -4077,8 +4324,8 @@ rb_io_gets_internal(VALUE io)
* or +nil+ if none.
* - But returns no more bytes than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.open('t.txt')
* # Chomp the lines.
@@ -4107,8 +4354,8 @@ rb_io_gets_m(int argc, VALUE *argv, VALUE io)
* call-seq:
* lineno -> integer
*
- * Returns the current line number for the stream.
- * See {Line Number}[rdoc-ref:IO@Line+Number].
+ * Returns the current line number for the stream;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*
*/
@@ -4126,8 +4373,8 @@ rb_io_lineno(VALUE io)
* call-seq:
* lineno = integer -> integer
*
- * Sets and returns the line number for the stream.
- * See {Line Number}[rdoc-ref:IO@Line+Number].
+ * Sets and returns the line number for the stream;
+ * see {Line Number}[rdoc-ref:IO@Line+Number].
*
*/
@@ -4144,12 +4391,14 @@ rb_io_set_lineno(VALUE io, VALUE lineno)
/*
* call-seq:
- * readline(sep = $/, **line_opts) -> string
- * readline(limit, **line_opts) -> string
- * readline(sep, limit, **line_opts) -> string
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
- * Reads a line as with IO#gets, but raises EOFError if already at end-of-file.
+ * Reads a line as with IO#gets, but raises EOFError if already at end-of-stream.
*
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted.
*/
static VALUE
@@ -4167,13 +4416,13 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
/*
* call-seq:
- * readlines(sep = $/, **line_opts) -> array
- * readlines(limit, **line_opts) -> array
- * readlines(sep, limit, **line_opts) -> array
+ * readlines(sep = $/, chomp: false) -> array
+ * readlines(limit, chomp: false) -> array
+ * readlines(sep, limit, chomp: false) -> array
*
- * Reads and returns all remaining line from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
+ * Reads and returns all remaining line from the stream;
* does not modify <tt>$_</tt>.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, returns lines
* as determined by line separator <tt>$/</tt>, or +nil+ if none:
@@ -4221,8 +4470,8 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
* - Returns lines as determined by line separator +sep+.
* - But returns no more bytes in a line than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.new('t.txt')
* f.readlines(chomp: true)
@@ -4256,15 +4505,15 @@ io_readlines(const struct getline_arg *arg, VALUE io)
/*
* call-seq:
- * each_line(sep = $/, **line_opts) {|line| ... } -> self
- * each_line(limit, **line_opts) {|line| ... } -> self
- * each_line(sep, limit, **line_opts) {|line| ... } -> self
+ * each_line(sep = $/, chomp: false) {|line| ... } -> self
+ * each_line(limit, chomp: false) {|line| ... } -> self
+ * each_line(sep, limit, chomp: false) {|line| ... } -> self
* each_line -> enumerator
*
- * Calls the block with each remaining line read from the stream
- * (see {Lines}[rdoc-ref:IO@Lines]);
- * does nothing if already at end-of-file;
+ * Calls the block with each remaining line read from the stream;
* returns +self+.
+ * Does nothing if already at end-of-stream;
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With no arguments given, reads lines
* as determined by line separator <tt>$/</tt>:
@@ -4320,7 +4569,7 @@ io_readlines(const struct getline_arg *arg, VALUE io)
*
* With only integer argument +limit+ given,
* limits the number of bytes in each line;
- * see {Line Limit}}[rdoc-ref:IO@Line+Limit]:
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit]:
*
* f = File.new('t.txt')
* f.each_line(8) {|line| p line }
@@ -4344,8 +4593,8 @@ io_readlines(const struct getline_arg *arg, VALUE io)
* - Calls with the next line as determined by line separator +sep+.
* - But returns no more bytes than are allowed by the limit.
*
- * For all forms above, optional keyword arguments +line_opts+ specify
- * {Line Options}[rdoc-ref:IO@Line+Options]:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* f = File.new('t.txt')
* f.each_line(chomp: true) {|line| p line }
@@ -4386,7 +4635,8 @@ rb_io_each_line(int argc, VALUE *argv, VALUE io)
* each_byte {|byte| ... } -> self
* each_byte -> enumerator
*
- * Calls the given block with each byte (0..255) in the stream; returns +self+:
+ * Calls the given block with each byte (0..255) in the stream; returns +self+.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.new('t.rus')
* a = []
@@ -4533,7 +4783,8 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
* each_char {|c| ... } -> self
* each_char -> enumerator
*
- * Calls the given block with each character in the stream; returns +self+:
+ * Calls the given block with each character in the stream; returns +self+.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.new('t.rus')
* a = []
@@ -4694,7 +4945,8 @@ rb_io_each_codepoint(VALUE io)
* getc -> character or nil
*
* Reads and returns the next 1-character string from the stream;
- * returns +nil+ if already at end-of-file:
+ * returns +nil+ if already at end-of-stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.open('t.txt')
* f.getc # => "F"
@@ -4726,7 +4978,8 @@ rb_io_getc(VALUE io)
* readchar -> string
*
* Reads and returns the next 1-character string from the stream;
- * raises EOFError if already at end-of-file:
+ * raises EOFError if already at end-of-stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* f = File.open('t.txt')
* f.readchar # => "F"
@@ -4755,7 +5008,8 @@ rb_io_readchar(VALUE io)
* getbyte -> integer or nil
*
* Reads and returns the next byte (in range 0..255) from the stream;
- * returns +nil+ if already at end-of-file:
+ * returns +nil+ if already at end-of-stream.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.open('t.txt')
* f.getbyte # => 70
@@ -4765,7 +5019,6 @@ rb_io_readchar(VALUE io)
* f.close
*
* Related: IO#readbyte (may raise EOFError).
- *
*/
VALUE
@@ -4799,7 +5052,8 @@ rb_io_getbyte(VALUE io)
* readbyte -> integer
*
* Reads and returns the next byte (in range 0..255) from the stream;
- * raises EOFError if already at end-of-file:
+ * raises EOFError if already at end-of-stream.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* f = File.open('t.txt')
* f.readbyte # => 70
@@ -4830,10 +5084,11 @@ rb_io_readbyte(VALUE io)
*
* Pushes back ("unshifts") the given data onto the stream's buffer,
* placing the data so that it is next to be read; returns +nil+.
+ * See {Byte IO}[rdoc-ref:IO@Byte+IO].
*
* Note that:
*
- * - Calling the method hs no effect with unbuffered reads (such as IO#sysread).
+ * - Calling the method has no effect with unbuffered reads (such as IO#sysread).
* - Calling #rewind on the stream discards the pushed-back data.
*
* When argument +integer+ is given, uses only its low-order byte:
@@ -4890,10 +5145,11 @@ rb_io_ungetbyte(VALUE io, VALUE b)
*
* Pushes back ("unshifts") the given data onto the stream's buffer,
* placing the data so that it is next to be read; returns +nil+.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* Note that:
*
- * - Calling the method hs no effect with unbuffered reads (such as IO#sysread).
+ * - Calling the method has no effect with unbuffered reads (such as IO#sysread).
* - Calling #rewind on the stream discards the pushed-back data.
*
* When argument +integer+ is given, interprets the integer as a character:
@@ -4974,8 +5230,10 @@ rb_io_ungetc(VALUE io, VALUE c)
* Returns +true+ if the stream is associated with a terminal device (tty),
* +false+ otherwise:
*
- * File.new('t.txt').isatty #=> false
- * File.new('/dev/tty').isatty #=> true
+ * f = File.new('t.txt').isatty #=> false
+ * f.close
+ * f = File.new('/dev/tty').isatty #=> true
+ * f.close
*
* IO#tty? is an alias for IO#isatty.
*
@@ -5109,13 +5367,13 @@ finish_writeconv(rb_io_t *fptr, int noalloc)
res = rb_econv_convert(fptr->writeconv, NULL, NULL, &dp, de, 0);
while (dp-ds) {
size_t remaining = dp-ds;
- long result = rb_write_internal(fptr, ds, remaining);
+ long result = rb_io_write_memory(fptr, ds, remaining);
if (result > 0) {
ds += result;
if ((size_t)result == remaining) break;
}
- else if (rb_io_maybe_wait_writable(errno, fptr->self, Qnil)) {
+ else if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
if (fptr->fd < 0)
return noalloc ? Qtrue : rb_exc_new3(rb_eIOError, rb_str_new_cstr(closed_stream));
}
@@ -5260,7 +5518,7 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl,
// VALUE scheduler = rb_fiber_scheduler_current();
// if (scheduler != Qnil) {
// VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self);
- // if (result != Qundef) done = 1;
+ // if (!UNDEF_P(result)) done = 1;
// }
// }
@@ -5456,12 +5714,32 @@ rb_io_close(VALUE io)
* call-seq:
* close -> nil
*
- * Closes the stream, if it is open, after flushing any buffered writes
- * to the operating system; does nothing if the stream is already closed.
- * A stream is automatically closed when claimed by the garbage collector.
+ * Closes the stream for both reading and writing
+ * if open for either or both; returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
+ *
+ * If the stream is open for writing, flushes any buffered writes
+ * to the operating system before closing.
*
- * If the stream was opened by IO.popen, #close sets global variable <tt>$?</tt>.
+ * If the stream was opened by IO.popen, sets global variable <tt>$?</tt>
+ * (child exit status).
*
+ * Example:
+ *
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close
+ * puts $?
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
+ *
+ * false
+ * pid 13760 exit 0
+ * true
+ *
+ * Related: IO#close_read, IO#close_write, IO#closed?.
*/
static VALUE
@@ -5499,7 +5777,7 @@ static VALUE
io_close(VALUE io)
{
VALUE closed = rb_check_funcall(io, rb_intern("closed?"), 0, 0);
- if (closed != Qundef && RTEST(closed)) return io;
+ if (!UNDEF_P(closed) && RTEST(closed)) return io;
rb_rescue2(io_call_close, io, ignore_closed_stream, io,
rb_eIOError, (VALUE)0);
return io;
@@ -5510,17 +5788,24 @@ io_close(VALUE io)
* closed? -> true or false
*
* Returns +true+ if the stream is closed for both reading and writing,
- * +false+ otherwise:
+ * +false+ otherwise.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
*
- * f = File.new('t.txt')
- * f.close # => nil
- * f.closed? # => true
- * f = IO.popen('/bin/sh','r+')
- * f.close_write # => nil
- * f.closed? # => false
- * f.close_read # => nil
- * f.closed? # => true
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
+ *
+ * false
+ * false
+ * true
*
+ * Related: IO#close_read, IO#close_write, IO#close.
*/
@@ -5547,15 +5832,32 @@ rb_io_closed(VALUE io)
* call-seq:
* close_read -> nil
*
- * Closes the read end of a duplexed stream (i.e., one that is both readable
- * and writable, such as a pipe); does nothing if already closed:
+ * Closes the stream for reading if open for reading;
+ * returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
*
- * f = IO.popen('/bin/sh','r+')
- * f.close_read
- * f.readlines # Raises IOError
+ * If the stream was opened by IO.popen and is also closed for writing,
+ * sets global variable <tt>$?</tt> (child exit status).
*
- * Raises an exception if the stream is not duplexed.
+ * Example:
+ *
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts $?
+ * puts pipe.closed?
+ * end
+ *
+ * Output:
*
+ * false
+ * false
+ * pid 14748 exit 0
+ * true
+ *
+ * Related: IO#close, IO#close_write, IO#closed?.
*/
static VALUE
@@ -5603,13 +5905,32 @@ rb_io_close_read(VALUE io)
* call-seq:
* close_write -> nil
*
- * Closes the write end of a duplexed stream (i.e., one that is both readable
- * and writable, such as a pipe); does nothing if already closed:
+ * Closes the stream for writing if open for writing;
+ * returns +nil+.
+ * See {Open and Closed Streams}[rdoc-ref:IO@Open+and+Closed+Streams].
+ *
+ * Flushes any buffered writes to the operating system before closing.
+ *
+ * If the stream was opened by IO.popen and is also closed for reading,
+ * sets global variable <tt>$?</tt> (child exit status).
*
- * f = IO.popen('/bin/sh', 'r+')
- * f.close_write
- * f.print 'nowhere' # Raises IOError.
+ * IO.popen('ruby', 'r+') do |pipe|
+ * puts pipe.closed?
+ * pipe.close_read
+ * puts pipe.closed?
+ * pipe.close_write
+ * puts $?
+ * puts pipe.closed?
+ * end
*
+ * Output:
+ *
+ * false
+ * false
+ * pid 15044 exit 0
+ * true
+ *
+ * Related: IO#close, IO#close_read, IO#closed?.
*/
static VALUE
@@ -5662,7 +5983,7 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
VALUE offset, ptrname;
int whence = SEEK_SET;
rb_io_t *fptr;
- off_t pos;
+ rb_off_t pos;
if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
whence = interpret_seek_whence(ptrname);
@@ -5722,7 +6043,7 @@ rb_io_syswrite(VALUE io, VALUE str)
tmp = rb_str_tmp_frozen_acquire(str);
RSTRING_GETMEM(tmp, ptr, len);
- n = rb_write_internal(fptr, ptr, len);
+ n = rb_io_write_memory(fptr, ptr, len);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
@@ -5768,9 +6089,11 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
iis.th = rb_thread_current();
iis.fptr = fptr;
iis.nonblock = 0;
+ iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = ilen;
- n = read_internal_locktmp(str, &iis);
+ iis.timeout = NULL;
+ n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
rb_sys_fail_path(fptr->pathv);
@@ -5790,7 +6113,7 @@ struct prdwr_internal_arg {
int fd;
void *buf;
size_t count;
- off_t offset;
+ rb_off_t offset;
};
#endif /* HAVE_PREAD || HAVE_PWRITE */
@@ -6005,7 +6328,7 @@ rb_io_ascii8bit_binmode(VALUE io)
* binmode -> self
*
* Sets the stream's data mode as binary
- * (see {Data Mode}[rdoc-ref:IO@Data+Mode]).
+ * (see {Data Mode}[rdoc-ref:File@Data+Mode]).
*
* A stream's data mode may not be changed from binary to text.
*
@@ -6029,7 +6352,7 @@ rb_io_binmode_m(VALUE io)
* binmode? -> true or false
*
* Returns +true+ if the stream is on binary mode, +false+ otherwise.
- * See {Data Mode}[rdoc-ref:IO@Data+Mode].
+ * See {Data Mode}[rdoc-ref:File@Data+Mode].
*
*/
static VALUE
@@ -6264,7 +6587,7 @@ rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_encoding **enc,
ext = rb_default_external_encoding();
default_ext = 1;
}
- if (ext == rb_ascii8bit_encoding()) {
+ if (rb_is_ascii8bit_enc(ext)) {
/* If external is ASCII-8BIT, no transcoding */
intern = NULL;
}
@@ -6372,21 +6695,21 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2
v = rb_hash_lookup2(opt, sym_extenc, Qundef);
if (v != Qnil) extenc = v;
v = rb_hash_lookup2(opt, sym_intenc, Qundef);
- if (v != Qundef) intenc = v;
+ if (!UNDEF_P(v)) intenc = v;
}
- if ((extenc != Qundef || intenc != Qundef) && !NIL_P(encoding)) {
+ if ((!UNDEF_P(extenc) || !UNDEF_P(intenc)) && !NIL_P(encoding)) {
if (!NIL_P(ruby_verbose)) {
int idx = rb_to_encoding_index(encoding);
if (idx >= 0) encoding = rb_enc_from_encoding(rb_enc_from_index(idx));
rb_warn("Ignoring encoding parameter '%"PRIsVALUE"': %s_encoding is used",
- encoding, extenc == Qundef ? "internal" : "external");
+ encoding, UNDEF_P(extenc) ? "internal" : "external");
}
encoding = Qnil;
}
- if (extenc != Qundef && !NIL_P(extenc)) {
+ if (!UNDEF_P(extenc) && !NIL_P(extenc)) {
extencoding = rb_to_encoding(extenc);
}
- if (intenc != Qundef) {
+ if (!UNDEF_P(intenc)) {
if (NIL_P(intenc)) {
/* internal_encoding: nil => no transcoding */
intencoding = (rb_encoding *)Qnil;
@@ -6419,7 +6742,7 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2
rb_io_ext_int_to_encs(rb_to_encoding(encoding), NULL, enc_p, enc2_p, 0);
}
}
- else if (extenc != Qundef || intenc != Qundef) {
+ else if (!UNDEF_P(extenc) || !UNDEF_P(intenc)) {
extracted = 1;
rb_io_ext_int_to_encs(extencoding, intencoding, enc_p, enc2_p, 0);
}
@@ -7460,7 +7783,7 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* and the block's value is assigned to global variable <tt>$?</tt> and returned.
*
* Optional argument +mode+ may be any valid \IO mode.
- * See IO@Modes.
+ * See {Access Modes}[rdoc-ref:File@Access+Modes].
*
* Required argument +cmd+ determines which of the following occurs:
*
@@ -7662,7 +7985,7 @@ popen_finish(VALUE port, VALUE klass)
if (NIL_P(port)) {
/* child */
if (rb_block_given_p()) {
- rb_yield(Qnil);
+ rb_protect(rb_yield, Qnil, NULL);
rb_io_flush(rb_ractor_stdout());
rb_io_flush(rb_ractor_stderr());
_exit(0);
@@ -7984,7 +8307,7 @@ io_reopen(VALUE io, VALUE nfile)
{
rb_io_t *fptr, *orig;
int fd, fd2;
- off_t pos = 0;
+ rb_off_t pos = 0;
nfile = rb_io_get_io(nfile);
GetOpenFile(io, fptr);
@@ -8204,7 +8527,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
rb_io_t *fptr, *orig;
int fd;
VALUE write_io;
- off_t pos;
+ rb_off_t pos;
io = rb_io_get_io(io);
if (!OBJ_INIT_COPY(dest, io)) return dest;
@@ -8218,6 +8541,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
fptr->encs = orig->encs;
fptr->pid = orig->pid;
fptr->lineno = orig->lineno;
+ fptr->timeout = orig->timeout;
if (!NIL_P(orig->pathv)) fptr->pathv = orig->pathv;
fptr_copy_finalizer(fptr, orig);
@@ -8328,6 +8652,7 @@ deprecated_str_setter(VALUE val, ID id, VALUE *var)
* Writes the given objects to the stream; returns +nil+.
* Appends the output record separator <tt>$OUTPUT_RECORD_SEPARATOR</tt>
* (<tt>$\\</tt>), if it is not +nil+.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* With argument +objects+ given, for each object:
*
@@ -8465,6 +8790,7 @@ rb_f_print(int argc, const VALUE *argv, VALUE _)
* putc(object) -> object
*
* Writes a character to the stream.
+ * See {Character IO}[rdoc-ref:IO@Character+IO].
*
* If +object+ is numeric, converts to integer if necessary,
* then writes the character whose code is the
@@ -8568,6 +8894,7 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
* returns +nil+.\
* Writes a newline after each that does not already end with a newline sequence.
* If called without arguments, writes a newline.
+ * See {Line IO}[rdoc-ref:IO@Line+IO].
*
* Note that each added newline is the character <tt>"\n"<//tt>,
* not the output record separator (<tt>$\\</tt>).
@@ -8609,7 +8936,6 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
- int i, n;
VALUE line, args[2];
/* if no argument given, print newline. */
@@ -8617,22 +8943,30 @@ rb_io_puts(int argc, const VALUE *argv, VALUE out)
rb_io_write(out, rb_default_rs);
return Qnil;
}
- for (i=0; i<argc; i++) {
+ for (int i = 0; i < argc; i++) {
+ // Convert the argument to a string:
if (RB_TYPE_P(argv[i], T_STRING)) {
line = argv[i];
- goto string;
}
- if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
+ else if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
continue;
}
- line = rb_obj_as_string(argv[i]);
- string:
- n = 0;
- args[n++] = line;
- if (RSTRING_LEN(line) == 0 ||
- !rb_str_end_with_asciichar(line, '\n')) {
+ else {
+ line = rb_obj_as_string(argv[i]);
+ }
+
+ // Write the line:
+ int n = 0;
+ if (RSTRING_LEN(line) == 0) {
args[n++] = rb_default_rs;
}
+ else {
+ args[n++] = line;
+ if (!rb_str_end_with_asciichar(line, '\n')) {
+ args[n++] = rb_default_rs;
+ }
+ }
+
rb_io_writev(out, n, args);
}
@@ -8887,6 +9221,7 @@ prep_io(int fd, int fmode, VALUE klass, const char *path)
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
+ fp->timeout = Qnil;
if (!io_check_tty(fp)) {
#ifdef __CYGWIN__
fp->mode |= FMODE_BINMODE;
@@ -8991,6 +9326,7 @@ rb_io_fptr_new(void)
fp->encs.ecflags = 0;
fp->encs.ecopts = Qnil;
fp->write_lock = Qnil;
+ fp->timeout = Qnil;
return fp;
}
@@ -9034,8 +9370,8 @@ rb_io_make_open_file(VALUE obj)
* io = IO.new(fd)
* io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.
*
- * Optional argument +mode+ (defaults to 'r') must specify a valid mode
- * see IO@Modes:
+ * Optional argument +mode+ (defaults to 'r') must specify a valid mode;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
*
* IO.new(fd, 'w') # => #<IO:fd 3>
* IO.new(fd, File::WRONLY) # => #<IO:fd 3>
@@ -9091,14 +9427,27 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
}
#endif
- if (!NIL_P(opt) && rb_hash_aref(opt, sym_autoclose) == Qfalse) {
- fmode |= FMODE_PREP;
+ VALUE path = Qnil;
+
+ if (!NIL_P(opt)) {
+ if (rb_hash_aref(opt, sym_autoclose) == Qfalse) {
+ fmode |= FMODE_PREP;
+ }
+
+ path = rb_hash_aref(opt, RB_ID2SYM(idPath));
+ if (!NIL_P(path)) {
+ StringValue(path);
+ path = rb_str_new_frozen(path);
+ }
}
+
MakeOpenFile(io, fp);
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
fp->encs = convconfig;
+ fp->pathv = path;
+ fp->timeout = Qnil;
clear_codeconv(fp);
io_check_tty(fp);
if (fileno(stdin) == fd)
@@ -9169,31 +9518,32 @@ rb_io_set_encoding_by_bom(VALUE io)
*
* Argument +path+ must be a valid file path:
*
- * File.new('/etc/fstab')
- * File.new('t.txt')
+ * f = File.new('/etc/fstab')
+ * f.close
+ * f = File.new('t.txt')
+ * f.close
*
- * Optional argument +mode+ (defaults to 'r') must specify a valid mode
- * see IO@Modes:
+ * Optional argument +mode+ (defaults to 'r') must specify a valid mode;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
*
- * File.new('t.tmp', 'w')
- * File.new('t.tmp', File::RDONLY)
+ * f = File.new('t.tmp', 'w')
+ * f.close
+ * f = File.new('t.tmp', File::RDONLY)
+ * f.close
*
* Optional argument +perm+ (defaults to 0666) must specify valid permissions
- * see {File Permissions}[rdoc-ref:IO@File+Permissions]:
+ * see {File Permissions}[rdoc-ref:File@File+Permissions]:
*
- * File.new('t.tmp', File::CREAT, 0644)
- * File.new('t.tmp', File::CREAT, 0444)
+ * f = File.new('t.tmp', File::CREAT, 0644)
+ * f.close
+ * f = File.new('t.tmp', File::CREAT, 0444)
+ * f.close
*
* Optional keyword arguments +opts+ specify:
*
* - {Open Options}[rdoc-ref:IO@Open+Options].
* - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
*
- * Examples:
- *
- * File.new('t.tmp', autoclose: true)
- * File.new('t.tmp', internal_encoding: nil)
- *
*/
static VALUE
@@ -9466,7 +9816,7 @@ io_wait(int argc, VALUE *argv, VALUE io)
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
- else if (timeout == Qundef) {
+ else if (UNDEF_P(timeout)) {
rb_time_interval(timeout = argv[i]);
}
else {
@@ -9474,7 +9824,7 @@ io_wait(int argc, VALUE *argv, VALUE io)
}
}
- if (timeout == Qundef) timeout = Qnil;
+ if (UNDEF_P(timeout)) timeout = Qnil;
if (events == 0) {
events = RUBY_IO_READABLE;
@@ -9976,9 +10326,9 @@ static VALUE argf_readline(int, VALUE *, VALUE);
/*
* call-seq:
- * readline(sep = $/, **line_opts) -> string
- * readline(limit, **line_opts) -> string
- * readline(sep, limit, **line_opts) -> string
+ * readline(sep = $/, chomp: false) -> string
+ * readline(limit, chomp: false) -> string
+ * readline(sep, limit, chomp: false) -> string
*
* Equivalent to method Kernel#gets, except that it raises an exception
* if called at end-of-stream:
@@ -9987,6 +10337,8 @@ static VALUE argf_readline(int, VALUE *, VALUE);
* ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
* in `readline': end of file reached (EOFError)
*
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted.
*/
static VALUE
@@ -10035,13 +10387,13 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
/*
* call-seq:
- * readlines(sep = $/, **line_opts) -> array
- * readlines(limit, **line_opts) -> array
- * readlines(sep, limit, **line_opts) -> array
+ * readlines(sep = $/, chomp: false, **enc_opts) -> array
+ * readlines(limit, chomp: false, **enc_opts) -> array
+ * readlines(sep, limit, chomp: false, **enc_opts) -> array
*
* Returns an array containing the lines returned by calling
- * Kernel#gets until the end-of-file is reached;
- * (see {Lines}[rdoc-ref:IO@Lines]).
+ * Kernel#gets until the end-of-stream is reached;
+ * (see {Line IO}[rdoc-ref:IO@Line+IO]).
*
* With only string argument +sep+ given,
* returns the remaining lines as determined by line separator +sep+,
@@ -10080,16 +10432,15 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
* With arguments +sep+ and +limit+ given, combines the two behaviors;
* see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit].
*
- * For all forms above, optional keyword arguments specify:
- *
- * - {Line Options}[rdoc-ref:IO@Line+Options].
- * - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
- *
- * Examples:
+ * Optional keyword argument +chomp+ specifies whether line separators
+ * are to be omitted:
*
* $ cat t.txt | ruby -e "p readlines(chomp: true)"
* ["First line", "Second line", "", "Fourth line", "Fifth line"]
*
+ * Optional keyword arguments +enc_opts+ specify encoding options;
+ * see {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
+ *
*/
static VALUE
@@ -10345,8 +10696,8 @@ static VALUE sym_normal, sym_sequential, sym_random,
struct io_advise_struct {
int fd;
int advice;
- off_t offset;
- off_t len;
+ rb_off_t offset;
+ rb_off_t len;
};
static VALUE
@@ -10393,7 +10744,7 @@ io_advise_sym_to_const(VALUE sym)
}
static VALUE
-do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len)
+do_io_advise(rb_io_t *fptr, VALUE advice, rb_off_t offset, rb_off_t len)
{
int rv;
struct io_advise_struct ias;
@@ -10483,7 +10834,7 @@ static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
VALUE advice, offset, len;
- off_t off, l;
+ rb_off_t off, l;
rb_io_t *fptr;
rb_scan_args(argc, argv, "12", &advice, &offset, &len);
@@ -10655,6 +11006,13 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
+ VALUE scheduler = rb_fiber_scheduler_current();
+ if (scheduler != Qnil) {
+ // It's optionally supported.
+ VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv);
+ if (!UNDEF_P(result)) return result;
+ }
+
VALUE timeout;
struct select_args args;
struct timeval timerec;
@@ -11518,6 +11876,8 @@ io_s_foreach(VALUE v)
struct getline_arg *arg = (void *)v;
VALUE str;
+ if (arg->limit == 0)
+ rb_raise(rb_eArgError, "invalid limit: 0 for foreach");
while (!NIL_P(str = rb_io_getline_1(arg->rs, arg->limit, arg->chomp, arg->io))) {
rb_lastline_set(str);
rb_yield(str);
@@ -12027,15 +12387,15 @@ rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
struct copy_stream_struct {
VALUE src;
VALUE dst;
- off_t copy_length; /* (off_t)-1 if not specified */
- off_t src_offset; /* (off_t)-1 if not specified */
+ rb_off_t copy_length; /* (rb_off_t)-1 if not specified */
+ rb_off_t src_offset; /* (rb_off_t)-1 if not specified */
rb_io_t *src_fptr;
rb_io_t *dst_fptr;
unsigned close_src : 1;
unsigned close_dst : 1;
int error_no;
- off_t total;
+ rb_off_t total;
const char *syserr;
const char *notimp;
VALUE th;
@@ -12078,7 +12438,7 @@ maygvl_copy_stream_continue_p(int has_gvl, struct copy_stream_struct *stp)
return FALSE;
}
-struct wait_for_single_fd {
+struct fiber_scheduler_wait_for_arguments {
VALUE scheduler;
rb_io_t *fptr;
@@ -12088,11 +12448,11 @@ struct wait_for_single_fd {
};
static void *
-rb_thread_fiber_scheduler_wait_for(void * _args)
+fiber_scheduler_wait_for(void * _arguments)
{
- struct wait_for_single_fd *args = (struct wait_for_single_fd *)_args;
+ struct fiber_scheduler_wait_for_arguments *arguments = (struct fiber_scheduler_wait_for_arguments *)_arguments;
- args->result = rb_fiber_scheduler_io_wait(args->scheduler, args->fptr->self, INT2NUM(args->events), Qnil);
+ arguments->result = rb_fiber_scheduler_io_wait(arguments->scheduler, arguments->fptr->self, INT2NUM(arguments->events), RUBY_IO_TIMEOUT_DEFAULT);
return NULL;
}
@@ -12102,12 +12462,12 @@ rb_thread_fiber_scheduler_wait_for(void * _args)
STATIC_ASSERT(pollin_expected, POLLIN == RB_WAITFD_IN);
STATIC_ASSERT(pollout_expected, POLLOUT == RB_WAITFD_OUT);
static int
-nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
+nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout)
{
VALUE scheduler = rb_fiber_scheduler_current_for_thread(th);
if (scheduler != Qnil) {
- struct wait_for_single_fd args = {.scheduler = scheduler, .fptr = fptr, .events = events};
- rb_thread_call_with_gvl(rb_thread_fiber_scheduler_wait_for, &args);
+ struct fiber_scheduler_wait_for_arguments args = {.scheduler = scheduler, .fptr = fptr, .events = events};
+ rb_thread_call_with_gvl(fiber_scheduler_wait_for, &args);
return RTEST(args.result);
}
@@ -12119,22 +12479,32 @@ nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
fds.fd = fd;
fds.events = events;
- return poll(&fds, 1, -1);
+ int timeout_milliseconds = -1;
+
+ if (timeout) {
+ timeout_milliseconds = (int)(timeout->tv_sec * 1000) + (int)(timeout->tv_usec / 1000);
+ }
+
+ return poll(&fds, 1, timeout_milliseconds);
}
#else /* !USE_POLL */
# define IOWAIT_SYSCALL "select"
static int
-nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
+nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval *timeout)
{
VALUE scheduler = rb_fiber_scheduler_current_for_thread(th);
if (scheduler != Qnil) {
- struct wait_for_single_fd args = {.scheduler = scheduler, .fptr = fptr, .events = events};
- rb_thread_call_with_gvl(rb_thread_fiber_scheduler_wait_for, &args);
+ struct fiber_scheduler_wait_for_arguments args = {.scheduler = scheduler, .fptr = fptr, .events = events};
+ rb_thread_call_with_gvl(fiber_scheduler_wait_for, &args);
return RTEST(args.result);
}
int fd = fptr->fd;
- if (fd == -1) return 0;
+
+ if (fd == -1) {
+ errno = EBADF;
+ return -1;
+ }
rb_fdset_t fds;
int ret;
@@ -12144,16 +12514,18 @@ nogvl_wait_for(VALUE th, rb_io_t *fptr, short events)
switch (events) {
case RB_WAITFD_IN:
- ret = rb_fd_select(fd + 1, &fds, 0, 0, 0);
+ ret = rb_fd_select(fd + 1, &fds, 0, 0, timeout);
break;
case RB_WAITFD_OUT:
- ret = rb_fd_select(fd + 1, 0, &fds, 0, 0);
+ ret = rb_fd_select(fd + 1, 0, &fds, 0, timeout);
break;
default:
VM_UNREACHABLE(nogvl_wait_for);
}
rb_fd_term(&fds);
+
+ // On timeout, this returns 0.
return ret;
}
#endif /* !USE_POLL */
@@ -12168,7 +12540,7 @@ maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
ret = RB_NUM2INT(rb_io_wait(stp->src, RB_INT2NUM(RUBY_IO_READABLE), Qnil));
}
else {
- ret = nogvl_wait_for(stp->th, stp->src_fptr, RB_WAITFD_IN);
+ ret = nogvl_wait_for(stp->th, stp->src_fptr, RB_WAITFD_IN, NULL);
}
} while (ret < 0 && maygvl_copy_stream_continue_p(has_gvl, stp));
@@ -12186,7 +12558,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
int ret;
do {
- ret = nogvl_wait_for(stp->th, stp->dst_fptr, RB_WAITFD_OUT);
+ ret = nogvl_wait_for(stp->th, stp->dst_fptr, RB_WAITFD_OUT, NULL);
} while (ret < 0 && maygvl_copy_stream_continue_p(0, stp));
if (ret < 0) {
@@ -12200,7 +12572,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
#ifdef USE_COPY_FILE_RANGE
static ssize_t
-simple_copy_file_range(int in_fd, off_t *in_offset, int out_fd, off_t *out_offset, size_t count, unsigned int flags)
+simple_copy_file_range(int in_fd, rb_off_t *in_offset, int out_fd, rb_off_t *out_offset, size_t count, unsigned int flags)
{
#ifdef HAVE_COPY_FILE_RANGE
return copy_file_range(in_fd, in_offset, out_fd, out_offset, count, flags);
@@ -12213,15 +12585,15 @@ static int
nogvl_copy_file_range(struct copy_stream_struct *stp)
{
ssize_t ss;
- off_t src_size;
- off_t copy_length, src_offset, *src_offset_ptr;
+ rb_off_t src_size;
+ rb_off_t copy_length, src_offset, *src_offset_ptr;
if (!S_ISREG(stp->src_stat.st_mode))
return 0;
src_size = stp->src_stat.st_size;
src_offset = stp->src_offset;
- if (src_offset >= (off_t)0) {
+ if (src_offset >= (rb_off_t)0) {
src_offset_ptr = &src_offset;
}
else {
@@ -12229,12 +12601,12 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
}
copy_length = stp->copy_length;
- if (copy_length < (off_t)0) {
- if (src_offset < (off_t)0) {
- off_t current_offset;
+ if (copy_length < (rb_off_t)0) {
+ if (src_offset < (rb_off_t)0) {
+ rb_off_t current_offset;
errno = 0;
current_offset = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (current_offset < (off_t)0 && errno) {
+ if (current_offset < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return (int)current_offset;
@@ -12249,7 +12621,7 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
retry_copy_file_range:
# if SIZEOF_OFF_T > SIZEOF_SIZE_T
/* we are limited by the 32-bit ssize_t return value on 32-bit */
- ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
+ ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
# else
ss = (ssize_t)copy_length;
# endif
@@ -12308,11 +12680,11 @@ nogvl_copy_file_range(struct copy_stream_struct *stp)
static int
nogvl_fcopyfile(struct copy_stream_struct *stp)
{
- off_t cur, ss = 0;
- const off_t src_offset = stp->src_offset;
+ rb_off_t cur, ss = 0;
+ const rb_off_t src_offset = stp->src_offset;
int ret;
- if (stp->copy_length >= (off_t)0) {
+ if (stp->copy_length >= (rb_off_t)0) {
/* copy_length can't be specified in fcopyfile(3) */
return 0;
}
@@ -12322,30 +12694,30 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
if (!S_ISREG(stp->dst_stat.st_mode))
return 0;
- if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */
+ if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (rb_off_t)0) /* if dst IO was already written */
return 0;
if (fcntl(stp->dst_fptr->fd, F_GETFL) & O_APPEND) {
/* fcopyfile(3) appends src IO to dst IO and then truncates
* dst IO to src IO's original size. */
- off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END);
+ rb_off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END);
lseek(stp->dst_fptr->fd, 0, SEEK_SET);
- if (end > (off_t)0) return 0;
+ if (end > (rb_off_t)0) return 0;
}
- if (src_offset > (off_t)0) {
- off_t r;
+ if (src_offset > (rb_off_t)0) {
+ rb_off_t r;
/* get current offset */
errno = 0;
cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (cur < (off_t)0 && errno) {
+ if (cur < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
errno = 0;
r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
@@ -12357,12 +12729,12 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
if (ret == 0) { /* success */
stp->total = ss;
- if (src_offset > (off_t)0) {
- off_t r;
+ if (src_offset > (rb_off_t)0) {
+ rb_off_t r;
errno = 0;
/* reset offset */
r = lseek(stp->src_fptr->fd, cur, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->error_no = errno;
return 1;
}
@@ -12393,7 +12765,7 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
# endif
static ssize_t
-simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
+simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count)
{
return sendfile(out_fd, in_fd, offset, (size_t)count);
}
@@ -12405,11 +12777,11 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
# define USE_SENDFILE
static ssize_t
-simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
+simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count)
{
int r;
- off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR);
- off_t sbytes;
+ rb_off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR);
+ rb_off_t sbytes;
# ifdef __APPLE__
r = sendfile(in_fd, out_fd, pos, &count, NULL, 0);
sbytes = count;
@@ -12435,9 +12807,9 @@ static int
nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
{
ssize_t ss;
- off_t src_size;
- off_t copy_length;
- off_t src_offset;
+ rb_off_t src_size;
+ rb_off_t copy_length;
+ rb_off_t src_offset;
int use_pread;
if (!S_ISREG(stp->src_stat.st_mode))
@@ -12450,17 +12822,17 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
#endif
src_offset = stp->src_offset;
- use_pread = src_offset >= (off_t)0;
+ use_pread = src_offset >= (rb_off_t)0;
copy_length = stp->copy_length;
- if (copy_length < (off_t)0) {
+ if (copy_length < (rb_off_t)0) {
if (use_pread)
copy_length = src_size - src_offset;
else {
- off_t cur;
+ rb_off_t cur;
errno = 0;
cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR);
- if (cur < (off_t)0 && errno) {
+ if (cur < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return (int)cur;
@@ -12472,7 +12844,7 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
retry_sendfile:
# if SIZEOF_OFF_T > SIZEOF_SIZE_T
/* we are limited by the 32-bit ssize_t return value on 32-bit */
- ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
+ ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
# else
ss = (ssize_t)copy_length;
# endif
@@ -12537,17 +12909,17 @@ static ssize_t
maygvl_read(int has_gvl, rb_io_t *fptr, void *buf, size_t count)
{
if (has_gvl)
- return rb_read_internal(fptr, buf, count);
+ return rb_io_read_memory(fptr, buf, count);
else
return read(fptr->fd, buf, count);
}
static ssize_t
-maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, off_t offset)
+maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, rb_off_t offset)
{
ssize_t ss;
retry_read:
- if (offset < (off_t)0) {
+ if (offset < (rb_off_t)0) {
ss = maygvl_read(has_gvl, stp->src_fptr, buf, len);
}
else {
@@ -12580,7 +12952,7 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
return ss;
#endif
}
- stp->syserr = offset < (off_t)0 ? "read" : "pread";
+ stp->syserr = offset < (rb_off_t)0 ? "read" : "pread";
stp->error_no = errno;
}
return ss;
@@ -12619,31 +12991,31 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp)
size_t len;
ssize_t ss;
int ret;
- off_t copy_length;
+ rb_off_t copy_length;
+ rb_off_t src_offset;
int use_eof;
- off_t src_offset;
int use_pread;
copy_length = stp->copy_length;
- use_eof = copy_length < (off_t)0;
+ use_eof = copy_length < (rb_off_t)0;
src_offset = stp->src_offset;
- use_pread = src_offset >= (off_t)0;
+ use_pread = src_offset >= (rb_off_t)0;
if (use_pread && stp->close_src) {
- off_t r;
+ rb_off_t r;
errno = 0;
r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET);
- if (r < (off_t)0 && errno) {
+ if (r < (rb_off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return;
}
- src_offset = (off_t)-1;
+ src_offset = (rb_off_t)-1;
use_pread = 0;
}
while (use_eof || 0 < copy_length) {
- if (!use_eof && copy_length < (off_t)sizeof(buf)) {
+ if (!use_eof && copy_length < (rb_off_t)sizeof(buf)) {
len = (size_t)copy_length;
}
else {
@@ -12655,7 +13027,7 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp)
src_offset += ss;
}
else {
- ss = maygvl_copy_stream_read(0, stp, buf, len, (off_t)-1);
+ ss = maygvl_copy_stream_read(0, stp, buf, len, (rb_off_t)-1);
}
if (ss <= 0) /* EOF or error */
return;
@@ -12710,8 +13082,8 @@ copy_stream_fallback_body(VALUE arg)
const int buflen = 16*1024;
VALUE n;
VALUE buf = rb_str_buf_new(buflen);
- off_t rest = stp->copy_length;
- off_t off = stp->src_offset;
+ rb_off_t rest = stp->copy_length;
+ rb_off_t off = stp->src_offset;
ID read_method = id_readpartial;
if (!stp->src_fptr) {
@@ -12723,7 +13095,8 @@ copy_stream_fallback_body(VALUE arg)
while (1) {
long numwrote;
long l;
- if (stp->copy_length < (off_t)0) {
+ rb_str_make_independent(buf);
+ if (stp->copy_length < (rb_off_t)0) {
l = buflen;
}
else {
@@ -12748,7 +13121,7 @@ copy_stream_fallback_body(VALUE arg)
return Qnil;
if (ss == 0)
rb_eof_error();
- if (off >= (off_t)0)
+ if (off >= (rb_off_t)0)
off += ss;
}
n = rb_io_write(stp->dst, buf);
@@ -12766,7 +13139,7 @@ copy_stream_fallback_body(VALUE arg)
static VALUE
copy_stream_fallback(struct copy_stream_struct *stp)
{
- if (!stp->src_fptr && stp->src_offset >= (off_t)0) {
+ if (!stp->src_fptr && stp->src_offset >= (rb_off_t)0) {
rb_raise(rb_eArgError, "cannot specify src_offset for non-IO");
}
rb_rescue2(copy_stream_fallback_body, (VALUE)stp,
@@ -12866,10 +13239,10 @@ copy_stream_body(VALUE arg)
if (stp->dst_fptr)
io_ascii8bit_binmode(stp->dst_fptr);
- if (stp->src_offset < (off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) {
+ if (stp->src_offset < (rb_off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) {
size_t len = stp->src_fptr->rbuf.len;
VALUE str;
- if (stp->copy_length >= (off_t)0 && stp->copy_length < (off_t)len) {
+ if (stp->copy_length >= (rb_off_t)0 && stp->copy_length < (rb_off_t)len) {
len = (size_t)stp->copy_length;
}
str = rb_str_buf_new(len);
@@ -12883,7 +13256,7 @@ copy_stream_body(VALUE arg)
rb_io_write(dst_io, str);
rb_str_resize(str, 0);
stp->total += len;
- if (stp->copy_length >= (off_t)0)
+ if (stp->copy_length >= (rb_off_t)0)
stp->copy_length -= len;
}
@@ -12996,12 +13369,12 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
st.dst_fptr = NULL;
if (NIL_P(length))
- st.copy_length = (off_t)-1;
+ st.copy_length = (rb_off_t)-1;
else
st.copy_length = NUM2OFFT(length);
if (NIL_P(src_offset))
- st.src_offset = (off_t)-1;
+ st.src_offset = (rb_off_t)-1;
else
st.src_offset = NUM2OFFT(src_offset);
@@ -13017,7 +13390,7 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
* Returns the Encoding object that represents the encoding of the stream,
* or +nil+ if the stream is in write mode and no encoding is specified.
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
*/
@@ -13045,7 +13418,7 @@ rb_io_external_encoding(VALUE io)
* if conversion is specified,
* or +nil+ otherwise.
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
*/
@@ -13064,7 +13437,7 @@ rb_io_internal_encoding(VALUE io)
* set_encoding(ext_enc, int_enc, **enc_opts) -> self
* set_encoding('ext_enc:int_enc', **enc_opts) -> self
*
- * See {Encodings}[rdoc-ref:IO@Encodings].
+ * See {Encodings}[rdoc-ref:File@Encodings].
*
* Argument +ext_enc+, if given, must be an Encoding object;
* it is assigned as the encoding for the stream.
@@ -13126,6 +13499,17 @@ global_argf_p(VALUE arg)
return arg == argf;
}
+typedef VALUE (*argf_encoding_func)(VALUE io);
+
+static VALUE
+argf_encoding(VALUE argf, argf_encoding_func func)
+{
+ if (!RTEST(ARGF.current_file)) {
+ return rb_enc_default_external();
+ }
+ return func(rb_io_check_io(ARGF.current_file));
+}
+
/*
* call-seq:
* ARGF.external_encoding -> encoding
@@ -13145,10 +13529,7 @@ global_argf_p(VALUE arg)
static VALUE
argf_external_encoding(VALUE argf)
{
- if (!RTEST(ARGF.current_file)) {
- return rb_enc_from_encoding(rb_default_external_encoding());
- }
- return rb_io_external_encoding(rb_io_check_io(ARGF.current_file));
+ return argf_encoding(argf, rb_io_external_encoding);
}
/*
@@ -13167,10 +13548,7 @@ argf_external_encoding(VALUE argf)
static VALUE
argf_internal_encoding(VALUE argf)
{
- if (!RTEST(ARGF.current_file)) {
- return rb_enc_from_encoding(rb_default_external_encoding());
- }
- return rb_io_internal_encoding(rb_io_check_io(ARGF.current_file));
+ return argf_encoding(argf, rb_io_internal_encoding);
}
/*
@@ -13762,7 +14140,7 @@ static void
argf_block_call(ID mid, int argc, VALUE *argv, VALUE argf)
{
VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_i, argf);
- if (ret != Qundef) ARGF.next_p = 1;
+ if (!UNDEF_P(ret)) ARGF.next_p = 1;
}
static VALUE
@@ -13778,7 +14156,7 @@ static void
argf_block_call_line(ID mid, int argc, VALUE *argv, VALUE argf)
{
VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_line_i, argf);
- if (ret != Qundef) ARGF.next_p = 1;
+ if (!UNDEF_P(ret)) ARGF.next_p = 1;
}
/*
@@ -14377,7 +14755,10 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* The global constant ARGF (also accessible as <tt>$<</tt>)
* provides an IO-like stream that allows access to all file paths
* found in ARGV (or found in STDIN if ARGV is empty).
- * Note that ARGF is not itself a subclass of \IO.
+ * ARGF is not itself a subclass of \IO.
+ *
+ * \Class StringIO provides an IO-like stream that handles a String.
+ * \StringIO is not itself a subclass of \IO.
*
* Important objects based on \IO include:
*
@@ -14395,20 +14776,23 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - Kernel#open: Returns a new \IO object connected to a given source:
* stream, file, or subprocess.
*
- * An \IO stream has:
+ * Like a \File stream, an \IO stream has:
*
* - A read/write mode, which may be read-only, write-only, or read/write;
- * see {Read/Write Mode}[rdoc-ref:IO@Read-2FWrite+Mode].
+ * see {Read/Write Mode}[rdoc-ref:File@Read-2FWrite+Mode].
* - A data mode, which may be text-only or binary;
- * see {Data Mode}[rdoc-ref:IO@Data+Mode].
+ * see {Data Mode}[rdoc-ref:File@Data+Mode].
+ * - Internal and external encodings;
+ * see {Encodings}[rdoc-ref:File@Encodings].
+ *
+ * And like other \IO streams, it has:
+ *
* - A position, which determines where in the stream the next
* read or write is to occur;
* see {Position}[rdoc-ref:IO@Position].
* - A line number, which is a special, line-oriented, "position"
* (different from the position mentioned above);
* see {Line Number}[rdoc-ref:IO@Line+Number].
- * - Internal and external encodings;
- * see {Encodings}[rdoc-ref:IO@Encodings].
*
* == Extension <tt>io/console</tt>
*
@@ -14418,221 +14802,146 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* == Example Files
*
- * Many examples here use these filenames and their corresponding files:
- *
- * - <tt>t.txt</tt>: A text-only file that is assumed to exist via:
- *
- * text = <<~EOT
- * First line
- * Second line
- *
- * Fourth line
- * Fifth line
- * EOT
- * File.write('t.txt', text)
- *
- * - <tt>t.dat</tt>: A data file that is assumed to exist via:
- *
- * data = "\u9990\u9991\u9992\u9993\u9994"
- * f = File.open('t.dat', 'wb:UTF-16')
- * f.write(data)
- * f.close
- *
- * - <tt>t.rus</tt>: A Russian-language text file that is assumed to exist via:
+ * Many examples here use these variables:
*
- * File.write('t.rus', "\u{442 435 441 442}")
+ * :include: doc/examples/files.rdoc
*
- * - <tt>t.tmp</tt>: A file that is assumed _not_ to exist.
- *
- * == Modes
- *
- * A number of \IO method calls must or may specify a _mode_ for the stream;
- * the mode determines how stream is to be accessible, including:
- *
- * - Whether the stream is to be read-only, write-only, or read-write.
- * - Whether the stream is positioned at its beginning or its end.
- * - Whether the stream treats data as text-only or binary.
- * - The external and internal encodings.
- *
- * === Read/Write Mode
- *
- * ==== Read/Write Mode Specified as an \Integer
+ * == Open Options
*
- * When +mode+ is an integer it must be one or more (combined by bitwise OR (<tt>|</tt>)
- * of the following modes:
+ * A number of \IO methods accept optional keyword arguments
+ * that determine how a new stream is to be opened:
*
- * - +File::RDONLY+: Open for reading only.
- * - +File::WRONLY+: Open for writing only.
- * - +File::RDWR+: Open for reading and writing.
- * - +File::APPEND+: Open for appending only.
- * - +File::CREAT+: Create file if it does not exist.
- * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists.
+ * - +:mode+: Stream mode.
+ * - +:flags+: \Integer file open flags;
+ * If +mode+ is also given, the two are bitwise-ORed.
+ * - +:external_encoding+: External encoding for the stream.
+ * - +:internal_encoding+: Internal encoding for the stream.
+ * <tt>'-'</tt> is a synonym for the default internal encoding.
+ * If the value is +nil+ no conversion occurs.
+ * - +:encoding+: Specifies external and internal encodings as <tt>'extern:intern'</tt>.
+ * - +:textmode+: If a truthy value, specifies the mode as text-only, binary otherwise.
+ * - +:binmode+: If a truthy value, specifies the mode as binary, text-only otherwise.
+ * - +:autoclose+: If a truthy value, specifies that the +fd+ will close
+ * when the stream closes; otherwise it remains open.
+ * - +:path:+ If a string value is provided, it is used in #inspect and is available as
+ * #path method.
*
- * Examples:
+ * Also available are the options offered in String#encode,
+ * which may control conversion between external internal encoding.
*
- * File.new('t.txt', File::RDONLY)
- * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
+ * == Basic \IO
*
- * Note: Method IO#set_encoding does not allow the mode to be specified as an integer.
+ * You can perform basic stream \IO with these methods,
+ * which typically operate on multi-byte strings:
*
- * ==== Read/Write Mode Specified As a \String
+ * - IO#read: Reads and returns some or all of the remaining bytes from the stream.
+ * - IO#write: Writes zero or more strings to the stream;
+ * each given object that is not already a string is converted via +to_s+.
*
- * When +mode+ is a string it must begin with one of the following:
+ * === Position
*
- * - <tt>'r'</tt>: Read-only stream, positioned at the beginning;
- * the stream cannot be changed to writable.
- * - <tt>'w'</tt>: Write-only stream, positioned at the beginning;
- * the stream cannot be changed to readable.
- * - <tt>'a'</tt>: Write-only stream, positioned at the end;
- * every write appends to the end;
- * the stream cannot be changed to readable.
- * - <tt>'r+'</tt>: Read-write stream, positioned at the beginning.
- * - <tt>'w+'</tt>: Read-write stream, positioned at the end.
- * - <tt>'a+'</tt>: Read-write stream, positioned at the end.
+ * An \IO stream has a nonnegative integer _position_,
+ * which is the byte offset at which the next read or write is to occur.
+ * A new stream has position zero (and line number zero);
+ * method +rewind+ resets the position (and line number) to zero.
*
- * For a writable file stream (that is, any except read-only),
- * the file is truncated to zero if it exists,
- * and is created if it does not exist.
+ * The relevant methods:
*
- * Examples:
+ * - IO#tell (aliased as +#pos+): Returns the current position (in bytes) in the stream.
+ * - IO#pos=: Sets the position of the stream to a given integer +new_position+ (in bytes).
+ * - IO#seek: Sets the position of the stream to a given integer +offset+ (in bytes),
+ * relative to a given position +whence+
+ * (indicating the beginning, end, or current position).
+ * - IO#rewind: Positions the stream at the beginning (also resetting the line number).
*
- * File.open('t.txt', 'r')
- * File.open('t.tmp', 'w')
+ * === Open and Closed Streams
*
- * === Data Mode
+ * A new \IO stream may be open for reading, open for writing, or both.
*
- * Either of the following may be suffixed to any of the string read/write modes above:
+ * A stream is automatically closed when claimed by the garbage collector.
*
- * - <tt>'t'</tt>: Text data; sets the default external encoding to +Encoding::UTF_8+;
- * on Windows, enables conversion between EOL and CRLF and enables interpreting +0x1A+
- * as an end-of-file marker.
- * - <tt>'b'</tt>: Binary data; sets the default external encoding to +Encoding::ASCII_8BIT+;
- * on Windows, suppresses conversion between EOL and CRLF and disables interpreting +0x1A+
- * as an end-of-file marker.
+ * Attempted reading or writing on a closed stream raises an exception.
*
- * If neither is given, the stream defaults to text data.
+ * The relevant methods:
*
- * Examples:
+ * - IO#close: Closes the stream for both reading and writing.
+ * - IO#close_read: Closes the stream for reading.
+ * - IO#close_write: Closes the stream for writing.
+ * - IO#closed?: Returns whether the stream is closed.
*
- * File.open('t.txt', 'rt')
- * File.open('t.dat', 'rb')
+ * === End-of-Stream
*
- * The following may be suffixed to any writable string mode above:
+ * You can query whether a stream is positioned at its end:
*
- * - <tt>'x'</tt>: Creates the file if it does not exist;
- * raises an exception if the file exists.
+ * - IO#eof? (also aliased as +#eof+): Returns whether the stream is at end-of-stream.
*
- * Example:
+ * You can reposition to end-of-stream by using method IO#seek:
*
- * File.open('t.tmp', 'wx')
- *
- * Note that when using integer flags to set the read/write mode, it's not
- * possible to also set the binary data mode by adding the File::BINARY flag
- * to the bitwise OR combination of integer flags. This is because, as
- * documented in File::Constants, the File::BINARY flag only disables line code
- * conversion, but does not change the external encoding at all.
- *
- * == Encodings
- *
- * Any of the string modes above may specify encodings --
- * either external encoding only or both external and internal encodings --
- * by appending one or both encoding names, separated by colons:
- *
- * f = File.new('t.dat', 'rb')
- * f.external_encoding # => #<Encoding:ASCII-8BIT>
- * f.internal_encoding # => nil
- * f = File.new('t.dat', 'rb:UTF-16')
- * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
- * f.internal_encoding # => nil
- * f = File.new('t.dat', 'rb:UTF-16:UTF-16')
- * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
- * f.internal_encoding # => #<Encoding:UTF-16>
+ * f = File.new('t.txt')
+ * f.eof? # => false
+ * f.seek(0, :END)
+ * f.eof? # => true
* f.close
*
- * The numerous encoding names are available in array Encoding.name_list:
- *
- * Encoding.name_list.size # => 175
- * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
+ * Or by reading all stream content (which is slower than using IO#seek):
*
- * When the external encoding is set,
- * strings read are tagged by that encoding
- * when reading, and strings written are converted to that
- * encoding when writing.
+ * f.rewind
+ * f.eof? # => false
+ * f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n"
+ * f.eof? # => true
*
- * When both external and internal encodings are set,
- * strings read are converted from external to internal encoding,
- * and strings written are converted from internal to external encoding.
- * For further details about transcoding input and output, see Encoding.
+ * == Line \IO
*
- * If the external encoding is <tt>'BOM|UTF-8'</tt>, <tt>'BOM|UTF-16LE'</tt>
- * or <tt>'BOM|UTF16-BE'</tt>, Ruby checks for
- * a Unicode BOM in the input document to help determine the encoding. For
- * UTF-16 encodings the file open mode must be binary.
- * If the BOM is found, it is stripped and the external encoding from the BOM is used.
+ * You can read an \IO stream line-by-line using these methods:
*
- * Note that the BOM-style encoding option is case insensitive,
- * so 'bom|utf-8' is also valid.)
+ * - IO#each_line: Reads each remaining line, passing it to the given block.
+ * - IO#gets: Returns the next line.
+ * - IO#readline: Like #gets, but raises an exception at end-of-stream.
+ * - IO#readlines: Returns all remaining lines in an array.
*
- * == Open Options
- *
- * A number of \IO methods accept optional keyword arguments
- * that determine how a new stream is to be opened:
+ * Each of these reader methods accepts:
*
- * - +:mode+: Stream mode.
- * - +:flags+: \Integer file open flags;
- * If +mode+ is also given, the two are bitwise-ORed.
- * - +:external_encoding+: External encoding for the stream.
- * - +:internal_encoding+: Internal encoding for the stream.
- * <tt>'-'</tt> is a synonym for the default internal encoding.
- * If the value is +nil+ no conversion occurs.
- * - +:encoding+: Specifies external and internal encodings as <tt>'extern:intern'</tt>.
- * - +:textmode+: If a truthy value, specifies the mode as text-only, binary otherwise.
- * - +:binmode+: If a truthy value, specifies the mode as binary, text-only otherwise.
- * - +:autoclose+: If a truthy value, specifies that the +fd+ will close
- * when the stream closes; otherwise it remains open.
+ * - An optional line separator, +sep+;
+ * see {Line Separator}[rdoc-ref:IO@Line+Separator].
+ * - An optional line-size limit, +limit+;
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit].
*
- * Also available are the options offered in String#encode,
- * which may control conversion between external internal encoding.
+ * For each of these reader methods, reading may begin mid-line,
+ * depending on the stream's position;
+ * see {Position}[rdoc-ref:IO@Position]:
*
- * == Lines
+ * f = File.new('t.txt')
+ * f.pos = 27
+ * f.each_line {|line| p line }
+ * f.close
*
- * Some reader methods in \IO are line-oriented;
- * such a method reads one or more lines,
- * which are separated by an implicit or explicit line separator.
+ * Output:
*
- * These methods include:
+ * "rth line\n"
+ * "Fifth line\n"
*
- * - Kernel#gets
- * - Kernel#readline
- * - Kernel#readlines
- * - IO.foreach
- * - IO.readlines
- * - IO#each_line
- * - IO#gets
- * - IO#readline
- * - IO#readlines
- * - ARGF.each
- * - ARGF.gets
- * - ARGF.readline
- * - ARGF.readlines
+ * You can write to an \IO stream line-by-line using this method:
*
- * Each of these methods returns +nil+ if called when already at end-of-stream,
- * except for IO#readline, which raises an exception.
+ * - IO#puts: Writes objects to the stream.
*
- * Each of these methods may be called with:
+ * === Line Separator
*
- * - An optional line separator, +sep+.
- * - An optional line-size limit, +limit+.
- * - Both +sep+ and +limit+.
+ * Each of these methods uses a <i>line separator</i>,
+ * which is the string that delimits lines:
*
- * === Line Separator
+ * - IO.foreach.
+ * - IO.readlines.
+ * - IO#each_line.
+ * - IO#gets.
+ * - IO#readline.
+ * - IO#readlines.
*
* The default line separator is the given by the global variable <tt>$/</tt>,
- * whose value is often <tt>"\n"</tt>.
+ * whose value is by default <tt>"\n"</tt>.
* The line to be read next is all data from the current position
* to the next line separator:
*
- * f = File.open('t.txt')
+ * f = File.new('t.txt')
* f.gets # => "First line\n"
* f.gets # => "Second line\n"
* f.gets # => "\n"
@@ -14667,9 +14976,18 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* === Line Limit
*
- * The line to be read may be further defined by an optional argument +limit+,
- * which specifies that the line may not be (much) longer than the given limit;
- * a multi-byte character will not be split, and so a line may be slightly longer
+ * Each of these methods uses a <i>line limit</i>,
+ * which specifies that the number of bytes returned may not be (much) longer
+ * than the given +limit+;
+ *
+ * - IO.foreach.
+ * - IO.readlines.
+ * - IO#each_line.
+ * - IO#gets.
+ * - IO#readline.
+ * - IO#readlines.
+ *
+ * A multi-byte character will not be split, and so a line may be slightly longer
* than the given limit.
*
* If +limit+ is not given, the line is determined only by +sep+.
@@ -14685,10 +15003,10 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
*
* # Text with 2-byte characters, which will not be split.
- * File.open('r.rus') {|f| f.gets(1).size } # => 1
- * File.open('r.rus') {|f| f.gets(2).size } # => 1
- * File.open('r.rus') {|f| f.gets(3).size } # => 2
- * File.open('r.rus') {|f| f.gets(4).size } # => 2
+ * File.open('t.rus') {|f| f.gets(1).size } # => 1
+ * File.open('t.rus') {|f| f.gets(2).size } # => 1
+ * File.open('t.rus') {|f| f.gets(3).size } # => 2
+ * File.open('t.rus') {|f| f.gets(4).size } # => 2
*
* === Line Separator and Line Limit
*
@@ -14705,17 +15023,38 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* === Line Number
*
- * A readable \IO stream has a _line_ _number_,
- * which is the non-negative integer line number
- * in the stream where the next read will occur.
+ * A readable \IO stream has a non-negative integer <i>line number</i>.
+ *
+ * The relevant methods:
+ *
+ * - IO#lineno: Returns the line number.
+ * - IO#lineno=: Resets and returns the line number.
*
- * A new stream is initially has line number +0+.
+ * Unless modified by a call to method IO#lineno=,
+ * the line number is the number of lines read
+ * by certain line-oriented methods,
+ * according to the given line separator +sep+:
*
- * \Method IO#lineno returns the line number.
+ * - IO.foreach: Increments the line number on each call to the block.
+ * - IO#each_line: Increments the line number on each call to the block.
+ * - IO#gets: Increments the line number.
+ * - IO#readline: Increments the line number.
+ * - IO#readlines: Increments the line number for each line read.
+ *
+ * A new stream is initially has line number zero (and position zero);
+ * method +rewind+ resets the line number (and position) to zero:
+ *
+ * f = File.new('t.txt')
+ * f.lineno # => 0
+ * f.gets # => "First line\n"
+ * f.lineno # => 1
+ * f.rewind
+ * f.lineno # => 0
+ * f.close
*
* Reading lines from a stream usually changes its line number:
*
- * f = File.open('t.txt', 'r')
+ * f = File.new('t.txt', 'r')
* f.lineno # => 0
* f.readline # => "This is line one.\n"
* f.lineno # => 1
@@ -14728,24 +15067,87 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* Iterating over lines in a stream usually changes its line number:
*
- * f = File.open('t.txt')
- * f.each_line do |line|
- * p "position=#{f.pos} eof?=#{f.eof?} line=#{line}"
- * end
- * f.close
+ * File.open('t.txt') do |f|
+ * f.each_line do |line|
+ * p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}"
+ * end
+ * end
*
* Output:
*
- * "position=19 eof?=false line=This is line one.\n"
- * "position=45 eof?=false line=This is the second line.\n"
- * "position=70 eof?=true line=This is the third line.\n"
+ * "position=11 eof?=false lineno=1"
+ * "position=23 eof?=false lineno=2"
+ * "position=24 eof?=false lineno=3"
+ * "position=36 eof?=false lineno=4"
+ * "position=47 eof?=true lineno=5"
*
- * === Line Options
+ * Unlike the stream's {position}[rdoc-ref:IO@Position],
+ * the line number does not affect where the next read or write will occur:
*
- * A number of \IO methods accept optional keyword arguments
- * that determine how lines in a stream are to be treated:
+ * f = File.new('t.txt')
+ * f.lineno = 1000
+ * f.lineno # => 1000
+ * f.gets # => "First line\n"
+ * f.lineno # => 1001
+ * f.close
+ *
+ * Associated with the line number is the global variable <tt>$.</tt>:
+ *
+ * - When a stream is opened, <tt>$.</tt> is not set;
+ * its value is left over from previous activity in the process:
+ *
+ * $. = 41
+ * f = File.new('t.txt')
+ * $. = 41
+ * # => 41
+ * f.close
+ *
+ * - When a stream is read, <tt>#.</tt> is set to the line number for that stream:
+ *
+ * f0 = File.new('t.txt')
+ * f1 = File.new('t.dat')
+ * f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
+ * $. # => 5
+ * f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"]
+ * $. # => 1
+ * f0.close
+ * f1.close
*
- * - +:chomp+: If +true+, line separators are omitted; default is +false+.
+ * - Methods IO#rewind and IO#seek do not affect <tt>$.</tt>:
+ *
+ * f = File.new('t.txt')
+ * f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
+ * $. # => 5
+ * f.rewind
+ * f.seek(0, :SET)
+ * $. # => 5
+ * f.close
+ *
+ * == Character \IO
+ *
+ * You can process an \IO stream character-by-character using these methods:
+ *
+ * - IO#getc: Reads and returns the next character from the stream.
+ * - IO#readchar: Like #getc, but raises an exception at end-of-stream.
+ * - IO#ungetc: Pushes back ("unshifts") a character or integer onto the stream.
+ * - IO#putc: Writes a character to the stream.
+ * - IO#each_char: Reads each remaining character in the stream,
+ * passing the character to the given block.
+ * == Byte \IO
+ *
+ * You can process an \IO stream byte-by-byte using these methods:
+ *
+ * - IO#getbyte: Returns the next 8-bit byte as an integer in range 0..255.
+ * - IO#readbyte: Like #getbyte, but raises an exception if at end-of-stream.
+ * - IO#ungetbyte: Pushes back ("unshifts") a byte back onto the stream.
+ * - IO#each_byte: Reads each remaining byte in the stream,
+ * passing the byte to the given block.
+ *
+ * == Codepoint \IO
+ *
+ * You can process an \IO stream codepoint-by-codepoint:
+ *
+ * - IO#each_codepoint: Reads each remaining codepoint, passing it to the given block.
*
* == What's Here
*
@@ -14794,11 +15196,11 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - #read_nonblock: the next _n_ bytes read from +self+ for a given _n_,
* in non-block mode.
* - #readbyte: Returns the next byte read from +self+;
- * same as #getbyte, but raises an exception on end-of-file.
+ * same as #getbyte, but raises an exception on end-of-stream.
* - #readchar: Returns the next character read from +self+;
- * same as #getc, but raises an exception on end-of-file.
+ * same as #getc, but raises an exception on end-of-stream.
* - #readline: Returns the next line read from +self+;
- * same as #getline, but raises an exception of end-of-file.
+ * same as #getline, but raises an exception of end-of-stream.
* - #readlines: Returns an array of all lines read read from +self+.
* - #readpartial: Returns up to the given number of bytes from +self+.
*
@@ -14858,7 +15260,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - #binmode?: Returns whether +self+ is in binary mode.
* - #close_on_exec?: Returns the close-on-exec flag for +self+.
* - #closed?: Returns whether +self+ is closed.
- * - #eof? (aliased as #eof): Returns whether +self+ is at end-of-file.
+ * - #eof? (aliased as #eof): Returns whether +self+ is at end-of-stream.
* - #external_encoding: Returns the external encoding object for +self+.
* - #fileno (aliased as #to_i): Returns the integer file descriptor for +self+
* - #internal_encoding: Returns the internal encoding object for +self+.
@@ -14949,6 +15351,8 @@ Init_IO(void)
rb_cIO = rb_define_class("IO", rb_cObject);
rb_include_module(rb_cIO, rb_mEnumerable);
+ rb_eIOTimeoutError = rb_define_class_under(rb_cIO, "TimeoutError", rb_eIOError);
+
rb_define_const(rb_cIO, "READABLE", INT2NUM(RUBY_IO_READABLE));
rb_define_const(rb_cIO, "WRITABLE", INT2NUM(RUBY_IO_WRITABLE));
rb_define_const(rb_cIO, "PRIORITY", INT2NUM(RUBY_IO_PRIORITY));
@@ -15047,23 +15451,26 @@ Init_IO(void)
rb_define_alias(rb_cIO, "to_i", "fileno");
rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0);
- rb_define_method(rb_cIO, "fsync", rb_io_fsync, 0);
- rb_define_method(rb_cIO, "fdatasync", rb_io_fdatasync, 0);
- rb_define_method(rb_cIO, "sync", rb_io_sync, 0);
- rb_define_method(rb_cIO, "sync=", rb_io_set_sync, 1);
+ rb_define_method(rb_cIO, "timeout", rb_io_timeout, 0);
+ rb_define_method(rb_cIO, "timeout=", rb_io_set_timeout, 1);
+
+ rb_define_method(rb_cIO, "fsync", rb_io_fsync, 0);
+ rb_define_method(rb_cIO, "fdatasync", rb_io_fdatasync, 0);
+ rb_define_method(rb_cIO, "sync", rb_io_sync, 0);
+ rb_define_method(rb_cIO, "sync=", rb_io_set_sync, 1);
- rb_define_method(rb_cIO, "lineno", rb_io_lineno, 0);
- rb_define_method(rb_cIO, "lineno=", rb_io_set_lineno, 1);
+ rb_define_method(rb_cIO, "lineno", rb_io_lineno, 0);
+ rb_define_method(rb_cIO, "lineno=", rb_io_set_lineno, 1);
- rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
+ rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
- rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
- rb_define_method(rb_cIO, "read", io_read, -1);
+ rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
+ rb_define_method(rb_cIO, "read", io_read, -1);
rb_define_method(rb_cIO, "write", io_write_m, -1);
- rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
- rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
- rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
- rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0);
+ rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
+ rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
+ rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
+ rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0);
rb_define_method(rb_cIO, "readchar", rb_io_readchar, 0);
rb_define_method(rb_cIO, "readbyte", rb_io_readbyte, 0);
rb_define_method(rb_cIO, "ungetbyte",rb_io_ungetbyte, 1);
@@ -15110,6 +15517,10 @@ Init_IO(void)
rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
rb_define_method(rb_cIO, "pid", rb_io_pid, 0);
+
+ rb_define_method(rb_cIO, "path", rb_io_path, 0);
+ rb_define_method(rb_cIO, "to_path", rb_io_path, 0);
+
rb_define_method(rb_cIO, "inspect", rb_io_inspect, 0);
rb_define_method(rb_cIO, "external_encoding", rb_io_external_encoding, 0);
@@ -15136,13 +15547,12 @@ Init_IO(void)
rb_gvar_ractor_local("$>");
rb_gvar_ractor_local("$stderr");
- rb_stdin = rb_io_prep_stdin();
- rb_stdout = rb_io_prep_stdout();
- rb_stderr = rb_io_prep_stderr();
-
rb_global_variable(&rb_stdin);
+ rb_stdin = rb_io_prep_stdin();
rb_global_variable(&rb_stdout);
+ rb_stdout = rb_io_prep_stdout();
rb_global_variable(&rb_stderr);
+ rb_stderr = rb_io_prep_stderr();
orig_stdout = rb_stdout;
orig_stderr = rb_stderr;
diff --git a/io_buffer.c b/io_buffer.c
index 6ce0dd13df..87b51c0b8c 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -11,9 +11,12 @@
#include "ruby/fiber/scheduler.h"
#include "internal.h"
-#include "internal/string.h"
+#include "internal/array.h"
#include "internal/bits.h"
#include "internal/error.h"
+#include "internal/numeric.h"
+#include "internal/string.h"
+#include "internal/thread.h"
VALUE rb_cIOBuffer;
VALUE rb_eIOBufferLockedError;
@@ -44,7 +47,7 @@ struct rb_io_buffer {
};
static inline void *
-io_buffer_map_memory(size_t size)
+io_buffer_map_memory(size_t size, int flags)
{
#if defined(_WIN32)
void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
@@ -53,7 +56,15 @@ io_buffer_map_memory(size_t size)
rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
}
#else
- void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ int mmap_flags = MAP_ANONYMOUS;
+ if (flags & RB_IO_BUFFER_SHARED) {
+ mmap_flags |= MAP_SHARED;
+ }
+ else {
+ mmap_flags |= MAP_PRIVATE;
+ }
+
+ void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
if (base == MAP_FAILED) {
rb_sys_fail("io_buffer_map_memory:mmap");
@@ -64,7 +75,7 @@ io_buffer_map_memory(size_t size)
}
static void
-io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags)
+io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
#if defined(_WIN32)
HANDLE file = (HANDLE)_get_osfhandle(descriptor);
@@ -73,7 +84,7 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
if (flags & RB_IO_BUFFER_READONLY) {
- data->flags |= RB_IO_BUFFER_READONLY;
+ buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect = PAGE_READWRITE;
@@ -85,11 +96,12 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
if (flags & RB_IO_BUFFER_PRIVATE) {
access |= FILE_MAP_COPY;
- data->flags |= RB_IO_BUFFER_PRIVATE;
+ buffer->flags |= RB_IO_BUFFER_PRIVATE;
}
else {
- // This buffer refers to external data.
- data->flags |= RB_IO_BUFFER_EXTERNAL;
+ // This buffer refers to external buffer.
+ buffer->flags |= RB_IO_BUFFER_EXTERNAL;
+ buffer->flags |= RB_IO_BUFFER_SHARED;
}
void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
@@ -99,23 +111,24 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
rb_sys_fail("io_buffer_map_file:MapViewOfFile");
}
- data->mapping = mapping;
+ buffer->mapping = mapping;
#else
int protect = PROT_READ, access = 0;
if (flags & RB_IO_BUFFER_READONLY) {
- data->flags |= RB_IO_BUFFER_READONLY;
+ buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect |= PROT_WRITE;
}
if (flags & RB_IO_BUFFER_PRIVATE) {
- data->flags |= RB_IO_BUFFER_PRIVATE;
+ buffer->flags |= RB_IO_BUFFER_PRIVATE;
}
else {
- // This buffer refers to external data.
- data->flags |= RB_IO_BUFFER_EXTERNAL;
+ // This buffer refers to external buffer.
+ buffer->flags |= RB_IO_BUFFER_EXTERNAL;
+ buffer->flags |= RB_IO_BUFFER_SHARED;
access |= MAP_SHARED;
}
@@ -126,10 +139,10 @@ io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t
}
#endif
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
- data->flags |= RB_IO_BUFFER_MAPPED;
+ buffer->flags |= RB_IO_BUFFER_MAPPED;
}
static inline void
@@ -159,18 +172,18 @@ io_buffer_experimental(void)
}
static void
-io_buffer_zero(struct rb_io_buffer *data)
+io_buffer_zero(struct rb_io_buffer *buffer)
{
- data->base = NULL;
- data->size = 0;
+ buffer->base = NULL;
+ buffer->size = 0;
#if defined(_WIN32)
- data->mapping = NULL;
+ buffer->mapping = NULL;
#endif
- data->source = Qnil;
+ buffer->source = Qnil;
}
static void
-io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
+io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
{
if (base) {
// If we are provided a pointer, we use it.
@@ -181,7 +194,7 @@ io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb
base = calloc(size, 1);
}
else if (flags & RB_IO_BUFFER_MAPPED) {
- base = io_buffer_map_memory(size);
+ base = io_buffer_map_memory(size, flags);
}
if (!base) {
@@ -193,41 +206,41 @@ io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb
return;
}
- data->base = base;
- data->size = size;
- data->flags = flags;
- data->source = source;
+ buffer->base = base;
+ buffer->size = size;
+ buffer->flags = flags;
+ buffer->source = source;
}
static int
-io_buffer_free(struct rb_io_buffer *data)
+io_buffer_free(struct rb_io_buffer *buffer)
{
- if (data->base) {
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
- free(data->base);
+ if (buffer->base) {
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
+ free(buffer->base);
}
- if (data->flags & RB_IO_BUFFER_MAPPED) {
- io_buffer_unmap(data->base, data->size);
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
+ io_buffer_unmap(buffer->base, buffer->size);
}
// Previously we had this, but we found out due to the way GC works, we
// can't refer to any other Ruby objects here.
- // if (RB_TYPE_P(data->source, T_STRING)) {
- // rb_str_unlocktmp(data->source);
+ // if (RB_TYPE_P(buffer->source, T_STRING)) {
+ // rb_str_unlocktmp(buffer->source);
// }
- data->base = NULL;
+ buffer->base = NULL;
#if defined(_WIN32)
- if (data->mapping) {
- CloseHandle(data->mapping);
- data->mapping = NULL;
+ if (buffer->mapping) {
+ CloseHandle(buffer->mapping);
+ buffer->mapping = NULL;
}
#endif
- data->size = 0;
- data->flags = 0;
- data->source = Qnil;
+ buffer->size = 0;
+ buffer->flags = 0;
+ buffer->source = Qnil;
return 1;
}
@@ -236,30 +249,30 @@ io_buffer_free(struct rb_io_buffer *data)
}
void
-rb_io_buffer_type_mark(void *_data)
+rb_io_buffer_type_mark(void *_buffer)
{
- struct rb_io_buffer *data = _data;
- rb_gc_mark(data->source);
+ struct rb_io_buffer *buffer = _buffer;
+ rb_gc_mark(buffer->source);
}
void
-rb_io_buffer_type_free(void *_data)
+rb_io_buffer_type_free(void *_buffer)
{
- struct rb_io_buffer *data = _data;
+ struct rb_io_buffer *buffer = _buffer;
- io_buffer_free(data);
+ io_buffer_free(buffer);
- free(data);
+ free(buffer);
}
size_t
-rb_io_buffer_type_size(const void *_data)
+rb_io_buffer_type_size(const void *_buffer)
{
- const struct rb_io_buffer *data = _data;
+ const struct rb_io_buffer *buffer = _buffer;
size_t total = sizeof(struct rb_io_buffer);
- if (data->flags) {
- total += data->size;
+ if (buffer->flags) {
+ total += buffer->size;
}
return total;
@@ -276,31 +289,138 @@ static const rb_data_type_t rb_io_buffer_type = {
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};
+// Extract an offset argument, which must be a positive integer.
+static inline size_t
+io_buffer_extract_offset(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Offset can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a length argument, which must be a positive integer.
+// Length is generally considered a mutable property of an object and
+// semantically should be considered a subset of "size" as a concept.
+static inline size_t
+io_buffer_extract_length(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Length can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a size argument, which must be a positive integer.
+// Size is generally considered an immutable property of an object.
+static inline size_t
+io_buffer_extract_size(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Size can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Compute the default length for a buffer, given an offset into that buffer.
+// The default length is the size of the buffer minus the offset. The offset
+// must be less than the size of the buffer otherwise the length will be
+// invalid; in that case, an ArgumentError exception will be raised.
+static inline size_t
+io_buffer_default_length(const struct rb_io_buffer *buffer, size_t offset)
+{
+ if (offset > buffer->size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ // Note that the "length" is computed by the size the offset.
+ return buffer->size - offset;
+}
+
+// Extract the optional length and offset arguments, returning the buffer.
+// The length and offset are optional, but if they are provided, they must be
+// positive integers. If the length is not provided, the default length is
+// computed from the buffer size and offset. If the offset is not provided, it
+// defaults to zero.
+static inline struct rb_io_buffer *
+io_buffer_extract_length_offset(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 2) {
+ *offset = io_buffer_extract_offset(argv[1]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 1 && !NIL_P(argv[0])) {
+ *length = io_buffer_extract_length(argv[0]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
+// Extract the optional offset and length arguments, returning the buffer.
+// Similar to `io_buffer_extract_length_offset` but with the order of
+// arguments reversed.
+static inline struct rb_io_buffer *
+io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offset, size_t *length)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *length = io_buffer_extract_length(argv[1]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
VALUE
rb_io_buffer_type_allocate(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_zero(data);
+ io_buffer_zero(buffer);
return instance;
}
-static VALUE
-io_buffer_for_make_instance(VALUE klass, VALUE string)
+static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_buffer_flags flags)
{
VALUE instance = rb_io_buffer_type_allocate(klass);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
+ flags |= RB_IO_BUFFER_EXTERNAL;
if (RB_OBJ_FROZEN(string))
flags |= RB_IO_BUFFER_READONLY;
- io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
+ if (!(flags & RB_IO_BUFFER_READONLY))
+ rb_str_modify(string);
+
+ io_buffer_initialize(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance;
}
@@ -309,6 +429,7 @@ struct io_buffer_for_yield_instance_arguments {
VALUE klass;
VALUE string;
VALUE instance;
+ enum rb_io_buffer_flags flags;
};
static VALUE
@@ -316,9 +437,9 @@ io_buffer_for_yield_instance(VALUE _arguments)
{
struct io_buffer_for_yield_instance_arguments *arguments = (struct io_buffer_for_yield_instance_arguments *)_arguments;
- rb_str_locktmp(arguments->string);
+ arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
- arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string);
+ rb_str_locktmp(arguments->string);
return rb_yield(arguments->instance);
}
@@ -345,14 +466,15 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
* Creates a IO::Buffer from the given string's memory. Without a block a
* frozen internal copy of the string is created efficiently and used as the
* buffer source. When a block is provided, the buffer is associated directly
- * with the string's internal data and updating the buffer will update the
+ * with the string's internal buffer and updating the buffer will update the
* string.
*
* Until #free is invoked on the buffer, either explicitly or via the garbage
* collector, the source string will be locked and cannot be modified.
*
* If the string is frozen, it will create a read-only buffer which cannot be
- * modified.
+ * modified. If the string is shared, it may trigger a copy-on-write when
+ * using the block form.
*
* string = 'test'
* buffer = IO::Buffer.for(string)
@@ -384,6 +506,7 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
.klass = klass,
.string = string,
.instance = Qnil,
+ .flags = 0,
};
return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
@@ -391,7 +514,7 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
else {
// This internally returns the source string if it's already frozen.
string = rb_str_tmp_frozen_acquire(string);
- return io_buffer_for_make_instance(klass, string);
+ return io_buffer_for_make_instance(klass, string, RB_IO_BUFFER_READONLY);
}
}
@@ -400,27 +523,27 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
{
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_initialize(data, base, size, flags, Qnil);
+ io_buffer_initialize(buffer, base, size, flags, Qnil);
return instance;
}
VALUE
-rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags)
+rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
io_buffer_experimental();
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
int descriptor = rb_io_descriptor(io);
- io_buffer_map_file(data, descriptor, size, offset, flags);
+ io_buffer_map_file(buffer, descriptor, size, offset, flags);
return instance;
}
@@ -437,48 +560,47 @@ rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags fl
* mapping, you need to open a file in read-write mode, and explicitly pass
* +flags+ argument without IO::Buffer::IMMUTABLE.
*
- * File.write('test.txt', 'test')
+ * Example:
*
- * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
- * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
+ * File.write('test.txt', 'test')
*
- * buffer.readonly? # => true
+ * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
+ * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
+ *
+ * buffer.readonly? # => true
*
- * buffer.get_string
- * # => "test"
+ * buffer.get_string
+ * # => "test"
*
- * buffer.set_string('b', 0)
- * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
+ * buffer.set_string('b', 0)
+ * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
*
- * # create read/write mapping: length 4 bytes, offset 0, flags 0
- * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
- * buffer.set_string('b', 0)
- * # => 1
+ * # create read/write mapping: length 4 bytes, offset 0, flags 0
+ * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
+ * buffer.set_string('b', 0)
+ * # => 1
*
- * # Check it
- * File.read('test.txt')
- * # => "best"
+ * # Check it
+ * File.read('test.txt')
+ * # => "best"
*
* Note that some operating systems may not have cache coherency between mapped
* buffers and file reads.
- *
*/
static VALUE
io_buffer_map(int argc, VALUE *argv, VALUE klass)
{
- if (argc < 1 || argc > 4) {
- rb_error_arity(argc, 2, 4);
- }
+ rb_check_arity(argc, 1, 4);
// We might like to handle a string path?
VALUE io = argv[0];
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- size = RB_NUM2SIZE(argv[1]);
+ size = io_buffer_extract_size(argv[1]);
}
else {
- off_t file_size = rb_file_size(io);
+ rb_off_t file_size = rb_file_size(io);
// Compiler can confirm that we handled file_size < 0 case:
if (file_size < 0) {
@@ -494,7 +616,8 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
}
}
- off_t offset = 0;
+ // This is the file offset, not the buffer offset:
+ rb_off_t offset = 0;
if (argc >= 3) {
offset = NUM2OFFT(argv[2]);
}
@@ -524,7 +647,7 @@ io_flags_for_size(size_t size)
* Create a new zero-filled IO::Buffer of +size+ bytes.
* By default, the buffer will be _internal_: directly allocated chunk
* of the memory. But if the requested +size+ is more than OS-specific
- * IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
+ * IO::Buffer::PAGE_SIZE, the buffer would be allocated using the
* virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
* on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
* as a second parameter.
@@ -533,34 +656,30 @@ io_flags_for_size(size_t size)
*
* buffer = IO::Buffer.new(4)
* # =>
- * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
- * # 0x00000000 00 00 00 00 ....
+ * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
+ * # 0x00000000 00 00 00 00 ....
*
* buffer.get_string(0, 1) # => "\x00"
*
* buffer.set_string("test")
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
* # 0x00000000 74 65 73 74 test
- *
*/
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
io_buffer_experimental();
- if (argc < 0 || argc > 2) {
- rb_error_arity(argc, 0, 2);
- }
+ rb_check_arity(argc, 0, 2);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
size_t size;
-
if (argc > 0) {
- size = RB_NUM2SIZE(argv[0]);
+ size = io_buffer_extract_size(argv[0]);
}
else {
size = RUBY_IO_BUFFER_DEFAULT_SIZE;
@@ -574,7 +693,7 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
flags |= io_flags_for_size(size);
}
- io_buffer_initialize(data, NULL, size, flags, Qnil);
+ io_buffer_initialize(buffer, NULL, size, flags, Qnil);
return self;
}
@@ -609,11 +728,11 @@ io_buffer_validate_slice(VALUE source, void *base, size_t size)
}
static int
-io_buffer_validate(struct rb_io_buffer *data)
+io_buffer_validate(struct rb_io_buffer *buffer)
{
- if (data->source != Qnil) {
+ if (buffer->source != Qnil) {
// Only slices incur this overhead, unfortunately... better safe than sorry!
- return io_buffer_validate_slice(data->source, data->base, data->size);
+ return io_buffer_validate_slice(buffer->source, buffer->base, buffer->size);
}
else {
return 1;
@@ -628,48 +747,51 @@ io_buffer_validate(struct rb_io_buffer *data)
*
* puts IO::Buffer.new(4) # uses to_s internally
* # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
- *
*/
VALUE
rb_io_buffer_to_s(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = rb_str_new_cstr("#<");
rb_str_append(result, rb_class_name(CLASS_OF(self)));
- rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);
+ rb_str_catf(result, " %p+%"PRIdSIZE, buffer->base, buffer->size);
- if (data->base == NULL) {
+ if (buffer->base == NULL) {
rb_str_cat2(result, " NULL");
}
- if (data->flags & RB_IO_BUFFER_EXTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
rb_str_cat2(result, " EXTERNAL");
}
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
rb_str_cat2(result, " INTERNAL");
}
- if (data->flags & RB_IO_BUFFER_MAPPED) {
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
rb_str_cat2(result, " MAPPED");
}
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_SHARED) {
+ rb_str_cat2(result, " SHARED");
+ }
+
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_str_cat2(result, " LOCKED");
}
- if (data->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY) {
rb_str_cat2(result, " READONLY");
}
- if (data->source != Qnil) {
+ if (buffer->source != Qnil) {
rb_str_cat2(result, " SLICE");
}
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_str_cat2(result, " INVALID");
}
@@ -719,15 +841,15 @@ io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first
static VALUE
rb_io_buffer_hexdump(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = Qnil;
- if (io_buffer_validate(data) && data->base) {
- result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);
+ if (io_buffer_validate(buffer) && buffer->base) {
+ result = rb_str_buf_new(buffer->size*3 + (buffer->size/16)*12 + 1);
- io_buffer_hexdump(result, 16, data->base, data->size, 1);
+ io_buffer_hexdump(result, 16, buffer->base, buffer->size, 1);
}
return result;
@@ -736,15 +858,15 @@ rb_io_buffer_hexdump(VALUE self)
VALUE
rb_io_buffer_inspect(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE result = rb_io_buffer_to_s(self);
- if (io_buffer_validate(data)) {
- // Limit the maximum size genearted by inspect.
- if (data->size <= 256) {
- io_buffer_hexdump(result, 16, data->base, data->size, 0);
+ if (io_buffer_validate(buffer)) {
+ // Limit the maximum size generated by inspect.
+ if (buffer->size <= 256) {
+ io_buffer_hexdump(result, 16, buffer->base, buffer->size, 0);
}
}
@@ -756,33 +878,31 @@ rb_io_buffer_inspect(VALUE self)
*
* Returns the size of the buffer that was explicitly set (on creation with ::new
* or on #resize), or deduced on buffer's creation from string or file.
- *
*/
VALUE
rb_io_buffer_size(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return SIZET2NUM(data->size);
+ return SIZET2NUM(buffer->size);
}
/*
* call-seq: valid? -> true or false
*
- * Returns whether the buffer data is accessible.
+ * Returns whether the buffer buffer is accessible.
*
* A buffer becomes invalid if it is a slice of another buffer which has been
* freed.
- *
*/
static VALUE
rb_io_buffer_valid_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(io_buffer_validate(data));
+ return RBOOL(io_buffer_validate(buffer));
}
/*
@@ -790,15 +910,14 @@ rb_io_buffer_valid_p(VALUE self)
*
* If the buffer was freed with #free or was never allocated in the first
* place.
- *
*/
static VALUE
rb_io_buffer_null_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->base == NULL);
+ return RBOOL(buffer->base == NULL);
}
/*
@@ -807,15 +926,14 @@ rb_io_buffer_null_p(VALUE self)
* If the buffer has 0 size: it is created by ::new with size 0, or with ::for
* from an empty string. (Note that empty files can't be mapped, so the buffer
* created with ::map will never be empty.)
- *
*/
static VALUE
rb_io_buffer_empty_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->size == 0);
+ return RBOOL(buffer->size == 0);
}
/*
@@ -828,15 +946,14 @@ rb_io_buffer_empty_p(VALUE self)
* memory.
*
* External buffer can't be resized.
- *
*/
static VALUE
rb_io_buffer_external_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
}
/*
@@ -854,15 +971,14 @@ rb_io_buffer_external_p(VALUE self)
*
* Internal buffers can be resized, and such an operation will typically
* invalidate all slices, but not always.
- *
*/
static VALUE
rb_io_buffer_internal_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
}
/*
@@ -877,15 +993,30 @@ rb_io_buffer_internal_p(VALUE self)
*
* Mapped buffers can usually be resized, and such an operation will typically
* invalidate all slices, but not always.
- *
*/
static VALUE
rb_io_buffer_mapped_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
+}
+
+/*
+ * call-seq: shared? -> true or false
+ *
+ * If the buffer is _shared_, meaning it references memory that can be shared
+ * with other processes (and thus might change without being modified
+ * locally).
+ */
+static VALUE
+rb_io_buffer_shared_p(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ return RBOOL(buffer->flags & RB_IO_BUFFER_SHARED);
}
/*
@@ -898,27 +1029,28 @@ rb_io_buffer_mapped_p(VALUE self)
* Locking is not thread safe, but is a semantic used to ensure buffers don't
* move while being used by a system call.
*
+ * Example:
+ *
* buffer.locked do
* buffer.write(io) # theoretical system call interface
* end
- *
*/
static VALUE
rb_io_buffer_locked_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
+ return RBOOL(buffer->flags & RB_IO_BUFFER_LOCKED);
}
int
rb_io_buffer_readonly_p(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- return data->flags & RB_IO_BUFFER_READONLY;
+ return buffer->flags & RB_IO_BUFFER_READONLY;
}
/*
@@ -928,7 +1060,6 @@ rb_io_buffer_readonly_p(VALUE self)
* #set_value, #set_string or #copy and similar.
*
* Frozen strings and read-only files create read-only buffers.
- *
*/
static VALUE
io_buffer_readonly_p(VALUE self)
@@ -936,32 +1067,44 @@ io_buffer_readonly_p(VALUE self)
return RBOOL(rb_io_buffer_readonly_p(self));
}
-VALUE
-rb_io_buffer_lock(VALUE self)
+static void
+io_buffer_lock(struct rb_io_buffer *buffer)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
-
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
}
- data->flags |= RB_IO_BUFFER_LOCKED;
-
- return self;
+ buffer->flags |= RB_IO_BUFFER_LOCKED;
}
VALUE
-rb_io_buffer_unlock(VALUE self)
+rb_io_buffer_lock(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_lock(buffer);
+
+ return self;
+}
- if (!(data->flags & RB_IO_BUFFER_LOCKED)) {
+static void
+io_buffer_unlock(struct rb_io_buffer *buffer)
+{
+ if (!(buffer->flags & RB_IO_BUFFER_LOCKED)) {
rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
}
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
+}
+
+VALUE
+rb_io_buffer_unlock(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_unlock(buffer);
return self;
}
@@ -969,11 +1112,11 @@ rb_io_buffer_unlock(VALUE self)
int
rb_io_buffer_try_unlock(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return 1;
}
@@ -988,6 +1131,14 @@ rb_io_buffer_try_unlock(VALUE self)
* can enter the lock. Also, locked buffer can't be changed with #resize or
* #free.
*
+ * The following operations acquire a lock: #resize, #free.
+ *
+ * Locking is not thread safe. It is designed as a safety net around
+ * non-blocking system calls. You can only share a buffer between threads with
+ * appropriate synchronisation techniques.
+ *
+ * Example:
+ *
* buffer = IO::Buffer.new(4)
* buffer.locked? #=> false
*
@@ -1003,28 +1154,22 @@ rb_io_buffer_try_unlock(VALUE self)
* buffer.set_string("test", 0)
* end
* end
- *
- * The following operations acquire a lock: #resize, #free.
- *
- * Locking is not thread safe. It is designed as a safety net around
- * non-blocking system calls. You can only share a buffer between threads with
- * appropriate synchronisation techniques.
*/
VALUE
rb_io_buffer_locked(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
}
- data->flags |= RB_IO_BUFFER_LOCKED;
+ buffer->flags |= RB_IO_BUFFER_LOCKED;
VALUE result = rb_yield(self);
- data->flags &= ~RB_IO_BUFFER_LOCKED;
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return result;
}
@@ -1039,47 +1184,73 @@ rb_io_buffer_locked(VALUE self)
*
* After the buffer is freed, no further operations can't be performed on it.
*
- * buffer = IO::Buffer.for('test')
- * buffer.free
- * # => #<IO::Buffer 0x0000000000000000+0 NULL>
+ * You can resize a freed buffer to re-allocate it.
*
- * buffer.get_value(:U8, 0)
- * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
+ * Example:
*
- * buffer.get_string
- * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
+ * buffer = IO::Buffer.for('test')
+ * buffer.free
+ * # => #<IO::Buffer 0x0000000000000000+0 NULL>
*
- * buffer.null?
- * # => true
+ * buffer.get_value(:U8, 0)
+ * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
*
- * You can resize a freed buffer to re-allocate it.
+ * buffer.get_string
+ * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
*
+ * buffer.null?
+ * # => true
*/
VALUE
rb_io_buffer_free(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
}
- io_buffer_free(data);
+ io_buffer_free(buffer);
return self;
}
+// Validate that access to the buffer is within bounds, assuming you want to
+// access length bytes from the specified offset.
static inline void
-io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length)
+io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
- if (offset + length > data->size) {
- rb_raise(rb_eArgError, "Specified offset+length exceeds data size!");
+ // We assume here that offset + length won't overflow:
+ if (offset + length > buffer->size) {
+ rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
}
}
+static VALUE
+rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_t length)
+{
+ io_buffer_validate_range(buffer, offset, length);
+
+ VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
+ struct rb_io_buffer *slice = NULL;
+ TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
+
+ slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
+ slice->base = (char*)buffer->base + offset;
+ slice->size = length;
+
+ // The source should be the root buffer:
+ if (buffer->source != Qnil)
+ slice->source = buffer->source;
+ else
+ slice->source = self;
+
+ return instance;
+}
+
/*
- * call-seq: slice(offset, length) -> io_buffer
+ * call-seq: slice([offset, [length]]) -> io_buffer
*
* Produce another IO::Buffer which is a slice (or view into) the current one
* starting at +offset+ bytes and going for +length+ bytes.
@@ -1087,76 +1258,76 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length
* The slicing happens without copying of memory, and the slice keeps being
* associated with the original buffer's source (string, or file), if any.
*
- * Raises RuntimeError if the <tt>offset+length<tt> is out of the current
+ * If the offset is not given, it will be zero. If the offset is negative, it
+ * will raise an ArgumentError.
+ *
+ * If the length is not given, the slice will be as long as the original
+ * buffer minus the specified offset. If the length is negative, it will raise
+ * an ArgumentError.
+ *
+ * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
* buffer's bounds.
*
- * string = 'test'
- * buffer = IO::Buffer.for(string)
+ * Example:
*
- * slice = buffer.slice(1, 2)
- * # =>
- * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
- * # 0x00000000 65 73 es
+ * string = 'test'
+ * buffer = IO::Buffer.for(string)
*
- * # Put "o" into 0s position of the slice
- * slice.set_string('o', 0)
- * slice
- * # =>
- * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
- * # 0x00000000 6f 73 os
+ * slice = buffer.slice
+ * # =>
+ * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
*
+ * buffer.slice(2)
+ * # =>
+ * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
+ * # 0x00000000 73 74 st
*
- * # it is also visible at position 1 of the original buffer
- * buffer
- * # =>
- * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
- * # 0x00000000 74 6f 73 74 tost
+ * slice = buffer.slice(1, 2)
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
+ * # 0x00000000 65 73 es
+ *
+ * # Put "o" into 0s position of the slice
+ * slice.set_string('o', 0)
+ * slice
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
+ * # 0x00000000 6f 73 os
*
- * # ...and original string
- * string
- * # => tost
+ * # it is also visible at position 1 of the original buffer
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
+ * # 0x00000000 74 6f 73 74 tost
*
+ * # ...and original string
+ * string
+ * # => tost
*/
-VALUE
-rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
+static VALUE
+io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
- // TODO fail on negative offets/lengths.
- size_t offset = NUM2SIZET(_offset);
- size_t length = NUM2SIZET(_length);
-
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ rb_check_arity(argc, 0, 2);
- io_buffer_validate_range(data, offset, length);
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
- VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
- struct rb_io_buffer *slice = NULL;
- TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
-
- slice->base = (char*)data->base + offset;
- slice->size = length;
-
- // The source should be the root buffer:
- if (data->source != Qnil)
- slice->source = data->source;
- else
- slice->source = self;
-
- return instance;
+ return rb_io_buffer_slice(buffer, self, offset, length);
}
int
rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (io_buffer_validate(data)) {
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (io_buffer_validate(buffer)) {
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
- return data->flags;
+ return buffer->flags;
}
}
@@ -1166,20 +1337,21 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
return 0;
}
-static void
-io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size)
+static inline void
+io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
{
- if (data->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY ||
+ (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
}
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
}
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
return;
}
@@ -1190,22 +1362,22 @@ io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *
void
rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_get_bytes_for_writing(data, base, size);
+ io_buffer_get_bytes_for_writing(buffer, base, size);
}
static void
-io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, size_t *size)
+io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
{
- if (!io_buffer_validate(data)) {
+ if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
}
- if (data->base) {
- *base = data->base;
- *size = data->size;
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
return;
}
@@ -1216,10 +1388,10 @@ io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, si
void
rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_get_bytes_for_reading(data, base, size);
+ io_buffer_get_bytes_for_reading(buffer, base, size);
}
/*
@@ -1227,26 +1399,27 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
*
* Transfers ownership to a new buffer, deallocating the current one.
*
- * buffer = IO::Buffer.new('test')
- * other = buffer.transfer
- * other
- * # =>
- * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
- * # 0x00000000 74 65 73 74 test
- * buffer
- * # =>
- * # #<IO::Buffer 0x0000000000000000+0 NULL>
- * buffer.null?
- * # => true
+ * Example:
*
+ * buffer = IO::Buffer.new('test')
+ * other = buffer.transfer
+ * other
+ * # =>
+ * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x0000000000000000+0 NULL>
+ * buffer.null?
+ * # => true
*/
VALUE
rb_io_buffer_transfer(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
}
@@ -1254,91 +1427,96 @@ rb_io_buffer_transfer(VALUE self)
struct rb_io_buffer *transferred;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
- *transferred = *data;
- io_buffer_zero(data);
+ *transferred = *buffer;
+ io_buffer_zero(buffer);
return instance;
}
static void
-io_buffer_resize_clear(struct rb_io_buffer *data, void* base, size_t size)
+io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
{
- if (size > data->size) {
- memset((unsigned char*)base+data->size, 0, size - data->size);
+ if (size > buffer->size) {
+ memset((unsigned char*)base+buffer->size, 0, size - buffer->size);
}
}
static void
-io_buffer_resize_copy(struct rb_io_buffer *data, size_t size)
+io_buffer_resize_copy(struct rb_io_buffer *buffer, size_t size)
{
// Slow path:
struct rb_io_buffer resized;
io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
- if (data->base) {
- size_t preserve = data->size;
+ if (buffer->base) {
+ size_t preserve = buffer->size;
if (preserve > size) preserve = size;
- memcpy(resized.base, data->base, preserve);
+ memcpy(resized.base, buffer->base, preserve);
- io_buffer_resize_clear(data, resized.base, size);
+ io_buffer_resize_clear(buffer, resized.base, size);
}
- io_buffer_free(data);
- *data = resized;
+ io_buffer_free(buffer);
+ *buffer = resized;
}
void
rb_io_buffer_resize(VALUE self, size_t size)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- if (data->flags & RB_IO_BUFFER_LOCKED) {
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
}
- if (data->base == NULL) {
- io_buffer_initialize(data, NULL, size, io_flags_for_size(size), Qnil);
+ if (buffer->base == NULL) {
+ io_buffer_initialize(buffer, NULL, size, io_flags_for_size(size), Qnil);
return;
}
- if (data->flags & RB_IO_BUFFER_EXTERNAL) {
+ if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
}
#if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
- if (data->flags & RB_IO_BUFFER_MAPPED) {
- void *base = mremap(data->base, data->size, size, MREMAP_MAYMOVE);
+ if (buffer->flags & RB_IO_BUFFER_MAPPED) {
+ void *base = mremap(buffer->base, buffer->size, size, MREMAP_MAYMOVE);
if (base == MAP_FAILED) {
rb_sys_fail("rb_io_buffer_resize:mremap");
}
- io_buffer_resize_clear(data, base, size);
+ io_buffer_resize_clear(buffer, base, size);
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
return;
}
#endif
- if (data->flags & RB_IO_BUFFER_INTERNAL) {
- void *base = realloc(data->base, size);
+ if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
+ if (size == 0) {
+ io_buffer_free(buffer);
+ return;
+ }
+
+ void *base = realloc(buffer->base, size);
if (!base) {
rb_sys_fail("rb_io_buffer_resize:realloc");
}
- io_buffer_resize_clear(data, base, size);
+ io_buffer_resize_clear(buffer, base, size);
- data->base = base;
- data->size = size;
+ buffer->base = base;
+ buffer->size = size;
return;
}
- io_buffer_resize_copy(data, size);
+ io_buffer_resize_copy(buffer, size);
}
/*
@@ -1352,18 +1530,17 @@ rb_io_buffer_resize(VALUE self, size_t size)
* buffer = IO::Buffer.new(4)
* buffer.set_string("test", 0)
* buffer.resize(8) # resize to 8 bytes
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
* # 0x00000000 74 65 73 74 00 00 00 00 test....
*
* External buffer (created with ::for), and locked buffer
* can not be resized.
- *
*/
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
- rb_io_buffer_resize(self, NUM2SIZET(size));
+ rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
@@ -1373,7 +1550,6 @@ io_buffer_resize(VALUE self, VALUE size)
*
* Buffers are compared by size and exact contents of the memory they are
* referencing using +memcmp+.
- *
*/
static VALUE
rb_io_buffer_compare(VALUE self, VALUE other)
@@ -1399,7 +1575,7 @@ static void
io_buffer_validate_type(size_t size, size_t offset)
{
if (offset > size) {
- rb_raise(rb_eArgError, "Type extends beyond end of buffer!");
+ rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
}
}
@@ -1449,8 +1625,8 @@ ruby_swapf64(double value)
return swap.value;
}
-#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
-static ID RB_IO_BUFFER_TYPE_##name; \
+#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
+static ID RB_IO_BUFFER_DATA_TYPE_##name; \
\
static VALUE \
io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
@@ -1471,67 +1647,125 @@ io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _val
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
memcpy((char*)base + *offset, &value, sizeof(type)); \
*offset += sizeof(type); \
-}
-
-DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
-DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
-
-DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
-DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
-DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
-DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+} \
+\
+enum { \
+ RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
+};
-DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
-DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
-DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
-DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
+IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
+
+IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
+
+IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
+
+IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+
+IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
+IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
+IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
+IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
+#undef IO_BUFFER_DECLARE_TYPE
+
+static inline size_t
+io_buffer_buffer_type_size(ID buffer_type)
+{
+#define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
+ IO_BUFFER_DATA_TYPE_SIZE(U8)
+ IO_BUFFER_DATA_TYPE_SIZE(S8)
+ IO_BUFFER_DATA_TYPE_SIZE(u16)
+ IO_BUFFER_DATA_TYPE_SIZE(U16)
+ IO_BUFFER_DATA_TYPE_SIZE(s16)
+ IO_BUFFER_DATA_TYPE_SIZE(S16)
+ IO_BUFFER_DATA_TYPE_SIZE(u32)
+ IO_BUFFER_DATA_TYPE_SIZE(U32)
+ IO_BUFFER_DATA_TYPE_SIZE(s32)
+ IO_BUFFER_DATA_TYPE_SIZE(S32)
+ IO_BUFFER_DATA_TYPE_SIZE(u64)
+ IO_BUFFER_DATA_TYPE_SIZE(U64)
+ IO_BUFFER_DATA_TYPE_SIZE(s64)
+ IO_BUFFER_DATA_TYPE_SIZE(S64)
+ IO_BUFFER_DATA_TYPE_SIZE(f32)
+ IO_BUFFER_DATA_TYPE_SIZE(F32)
+ IO_BUFFER_DATA_TYPE_SIZE(f64)
+ IO_BUFFER_DATA_TYPE_SIZE(F64)
+#undef IO_BUFFER_DATA_TYPE_SIZE
-DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
-DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
-DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
-DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+ rb_raise(rb_eArgError, "Invalid type name!");
+}
-DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
-DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
-DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
-DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
-#undef DECLARE_TYPE
+/*
+ * call-seq:
+ * size_of(buffer_type) -> byte size
+ * size_of(array of buffer_type) -> byte size
+ *
+ * Returns the size of the given buffer type(s) in bytes.
+ *
+ * Example:
+ *
+ * IO::Buffer.size_of(:u32) # => 4
+ * IO::Buffer.size_of([:u32, :u32]) # => 8
+ */
+static VALUE
+io_buffer_size_of(VALUE klass, VALUE buffer_type)
+{
+ if (RB_TYPE_P(buffer_type, T_ARRAY)) {
+ size_t total = 0;
+ for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
+ total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
+ }
+ return SIZET2NUM(total);
+ }
+ else {
+ return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
+ }
+}
-VALUE
-rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
+static inline VALUE
+rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *offset)
{
-#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset);
- READ_TYPE(U8);
- READ_TYPE(S8);
+#define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
+ IO_BUFFER_GET_VALUE(U8)
+ IO_BUFFER_GET_VALUE(S8)
- READ_TYPE(u16);
- READ_TYPE(U16);
- READ_TYPE(s16);
- READ_TYPE(S16);
+ IO_BUFFER_GET_VALUE(u16)
+ IO_BUFFER_GET_VALUE(U16)
+ IO_BUFFER_GET_VALUE(s16)
+ IO_BUFFER_GET_VALUE(S16)
- READ_TYPE(u32);
- READ_TYPE(U32);
- READ_TYPE(s32);
- READ_TYPE(S32);
+ IO_BUFFER_GET_VALUE(u32)
+ IO_BUFFER_GET_VALUE(U32)
+ IO_BUFFER_GET_VALUE(s32)
+ IO_BUFFER_GET_VALUE(S32)
- READ_TYPE(u64);
- READ_TYPE(U64);
- READ_TYPE(s64);
- READ_TYPE(S64);
+ IO_BUFFER_GET_VALUE(u64)
+ IO_BUFFER_GET_VALUE(U64)
+ IO_BUFFER_GET_VALUE(s64)
+ IO_BUFFER_GET_VALUE(S64)
- READ_TYPE(f32);
- READ_TYPE(F32);
- READ_TYPE(f64);
- READ_TYPE(F64);
-#undef READ_TYPE
+ IO_BUFFER_GET_VALUE(f32)
+ IO_BUFFER_GET_VALUE(F32)
+ IO_BUFFER_GET_VALUE(f64)
+ IO_BUFFER_GET_VALUE(F64)
+#undef IO_BUFFER_GET_VALUE
rb_raise(rb_eArgError, "Invalid type name!");
}
/*
- * call-seq: get_value(type, offset) -> numeric
+ * call-seq: get_value(buffer_type, offset) -> numeric
*
- * Read from buffer a value of +type+ at +offset+. +type+ should be one
+ * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
* of symbols:
*
* * +:U8+: unsigned integer, 1 byte
@@ -1553,53 +1787,253 @@ rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
* * +:f64+: double, 8 bytes, little-endian
* * +:F64+: double, 8 bytes, big-endian
*
+ * A buffer type refers specifically to the type of binary buffer that is stored
+ * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
+ * integer in little-endian format.
+ *
* Example:
*
* string = [1.5].pack('f')
* # => "\x00\x00\xC0?"
* IO::Buffer.for(string).get_value(:f32, 0)
* # => 1.5
- *
*/
static VALUE
io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
const void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
- return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
+ return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
}
-void
-rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value)
+/*
+ * call-seq: get_values(buffer_types, offset) -> array
+ *
+ * Similar to #get_value, except that it can handle multiple buffer types and
+ * returns an array of values.
+ *
+ * Example:
+ *
+ * string = [1.5, 2.5].pack('ff')
+ * IO::Buffer.for(string).get_values([:f32, :f32], 0)
+ * # => [1.5, 2.5]
+ */
+static VALUE
+io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
+{
+ size_t offset = io_buffer_extract_offset(_offset);
+
+ const void *base;
+ size_t size;
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
+ }
+
+ VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));
+
+ for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
+ VALUE type = rb_ary_entry(buffer_types, i);
+ VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
+ rb_ary_push(array, value);
+ }
+
+ return array;
+}
+
+// Extract a count argument, which must be a positive integer.
+// Count is generally considered relative to the number of things.
+static inline size_t
+io_buffer_extract_count(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Count can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+static inline void
+io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
+{
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *count = io_buffer_extract_count(argv[1]);
+ }
+ else {
+ if (*offset > size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
+ }
+}
+
+/*
+ * call-seq:
+ * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
+ * each(buffer_type, [offset, [count]]) -> enumerator
+ *
+ * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
+ * from +offset+.
+ *
+ * If +count+ is given, only +count+ values will be yielded.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
+ * puts "#{offset}: #{value}"
+ * end
+ * # 2: 108
+ * # 3: 108
+ */
+static VALUE
+io_buffer_each(int argc, VALUE *argv, VALUE self)
{
-#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;}
- WRITE_TYPE(U8);
- WRITE_TYPE(S8);
+ RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
+
+ const void *base;
+ size_t size;
+
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ ID buffer_type;
+ if (argc >= 1) {
+ buffer_type = RB_SYM2ID(argv[0]);
+ }
+ else {
+ buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
+ }
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
- WRITE_TYPE(u16);
- WRITE_TYPE(U16);
- WRITE_TYPE(s16);
- WRITE_TYPE(S16);
+ for (size_t i = 0; i < count; i++) {
+ size_t current_offset = offset;
+ VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
+ rb_yield_values(2, SIZET2NUM(current_offset), value);
+ }
- WRITE_TYPE(u32);
- WRITE_TYPE(U32);
- WRITE_TYPE(s32);
- WRITE_TYPE(S32);
+ return self;
+}
- WRITE_TYPE(u64);
- WRITE_TYPE(U64);
- WRITE_TYPE(s64);
- WRITE_TYPE(S64);
+/*
+ * call-seq: values(buffer_type, [offset, [count]]) -> array
+ *
+ * Returns an array of values of +buffer_type+ starting from +offset+.
+ *
+ * If +count+ is given, only +count+ values will be returned.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").values(:U8, 2, 2)
+ * # => [108, 108]
+ */
+static VALUE
+io_buffer_values(int argc, VALUE *argv, VALUE self)
+{
+ const void *base;
+ size_t size;
- WRITE_TYPE(f32);
- WRITE_TYPE(F32);
- WRITE_TYPE(f64);
- WRITE_TYPE(F64);
-#undef WRITE_TYPE
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ ID buffer_type;
+ if (argc >= 1) {
+ buffer_type = RB_SYM2ID(argv[0]);
+ }
+ else {
+ buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
+ }
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
+
+ VALUE array = rb_ary_new_capa(count);
+
+ for (size_t i = 0; i < count; i++) {
+ VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
+ rb_ary_push(array, value);
+ }
+
+ return array;
+}
+
+/*
+ * call-seq:
+ * each_byte([offset, [count]]) {|offset, byte| ...} -> self
+ * each_byte([offset, [count]]) -> enumerator
+ *
+ * Iterates over the buffer, yielding each byte starting from +offset+.
+ *
+ * If +count+ is given, only +count+ bytes will be yielded.
+ *
+ * Example:
+ *
+ * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
+ * puts "#{offset}: #{byte}"
+ * end
+ * # 2: 108
+ * # 3: 108
+ */
+static VALUE
+io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
+{
+ RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
+
+ const void *base;
+ size_t size;
+
+ rb_io_buffer_get_bytes_for_reading(self, &base, &size);
+
+ size_t offset, count;
+ io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
+
+ for (size_t i = 0; i < count; i++) {
+ unsigned char *value = (unsigned char *)base + i + offset;
+ rb_yield(RB_INT2FIX(*value));
+ }
+
+ return self;
+}
+
+static inline void
+rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *offset, VALUE value)
+{
+#define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;}
+ IO_BUFFER_SET_VALUE(U8);
+ IO_BUFFER_SET_VALUE(S8);
+
+ IO_BUFFER_SET_VALUE(u16);
+ IO_BUFFER_SET_VALUE(U16);
+ IO_BUFFER_SET_VALUE(s16);
+ IO_BUFFER_SET_VALUE(S16);
+
+ IO_BUFFER_SET_VALUE(u32);
+ IO_BUFFER_SET_VALUE(U32);
+ IO_BUFFER_SET_VALUE(s32);
+ IO_BUFFER_SET_VALUE(S32);
+
+ IO_BUFFER_SET_VALUE(u64);
+ IO_BUFFER_SET_VALUE(U64);
+ IO_BUFFER_SET_VALUE(s64);
+ IO_BUFFER_SET_VALUE(S64);
+
+ IO_BUFFER_SET_VALUE(f32);
+ IO_BUFFER_SET_VALUE(F32);
+ IO_BUFFER_SET_VALUE(f64);
+ IO_BUFFER_SET_VALUE(F64);
+#undef IO_BUFFER_SET_VALUE
rb_raise(rb_eArgError, "Invalid type name!");
}
@@ -1611,13 +2045,15 @@ rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VA
* symbols described in #get_value.
*
* buffer = IO::Buffer.new(8)
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00
+ *
* buffer.set_value(:U8, 1, 111)
* # => 1
+ *
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
* # 0x00000000 00 6f 00 00 00 00 00 00 .o......
*
@@ -1625,37 +2061,84 @@ rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VA
*
* buffer = IO::Buffer.new(8)
* buffer.set_value(:U32, 0, 2.5)
+ *
* buffer
- * # =>
- * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
- * # 0x00000000 00 00 00 02 00 00 00 00
- * # ^^ the same as if we'd pass just integer 2
+ * # =>
+ * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
+ * # 0x00000000 00 00 00 02 00 00 00 00
+ * # ^^ the same as if we'd pass just integer 2
*/
static VALUE
io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_writing(self, &base, &size);
- rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);
+ rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+
+ return SIZET2NUM(offset);
+}
+
+/*
+ * call-seq: set_values(buffer_types, offset, values) -> offset
+ *
+ * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
+ * should be an array of symbols as described in #get_value. +values+ should
+ * be an array of values to write.
+ *
+ * Example:
+ *
+ * buffer = IO::Buffer.new(8)
+ * buffer.set_values([:U8, :U16], 0, [1, 2])
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
+ * # 0x00000000 01 00 02 00 00 00 00 00 ........
+ */
+static VALUE
+io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
+{
+ if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
+ }
+
+ if (!RB_TYPE_P(values, T_ARRAY)) {
+ rb_raise(rb_eArgError, "Argument values should be an array!");
+ }
+
+ if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
+ rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
+ }
+
+ size_t offset = io_buffer_extract_offset(_offset);
+
+ void *base;
+ size_t size;
+ rb_io_buffer_get_bytes_for_writing(self, &base, &size);
+
+ for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
+ VALUE type = rb_ary_entry(buffer_types, i);
+ VALUE value = rb_ary_entry(values, i);
+ rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+ }
return SIZET2NUM(offset);
}
static void
-io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
+io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
{
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- io_buffer_validate_range(data, offset, length);
+ io_buffer_validate_range(buffer, offset, length);
if (source_offset + length > source_size) {
- rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
+ rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
@@ -1663,23 +2146,20 @@ io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_ba
// (offset, length, source_offset) -> length
static VALUE
-io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t source_size, int argc, VALUE *argv)
+io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
{
- size_t offset;
+ size_t offset = 0;
size_t length;
size_t source_offset;
// The offset we copy into the buffer:
if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
- else {
- offset = 0;
+ offset = io_buffer_extract_offset(argv[0]);
}
// The offset we start from within the string:
if (argc >= 3) {
- source_offset = NUM2SIZET(argv[2]);
+ source_offset = io_buffer_extract_offset(argv[2]);
if (source_offset > source_size) {
rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
@@ -1691,14 +2171,14 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s
// The length we are going to copy:
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
+ length = io_buffer_extract_length(argv[1]);
}
else {
// Default to the source offset -> source size:
length = source_size - source_offset;
}
- io_buffer_memcpy(data, offset, source_base, source_offset, source_size, length);
+ io_buffer_memcpy(buffer, offset, source_base, source_offset, source_size, length);
return SIZET2NUM(length);
}
@@ -1719,54 +2199,53 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s
* # =>
* # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
* # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
- *
*/
static VALUE
rb_io_buffer_initialize_copy(VALUE self, VALUE source)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
const void *source_base;
size_t source_size;
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- io_buffer_initialize(data, NULL, source_size, io_flags_for_size(source_size), Qnil);
+ io_buffer_initialize(buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
- return io_buffer_copy_from(data, source_base, source_size, 0, NULL);
+ return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
}
/*
* call-seq:
* copy(source, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy data from a source IO::Buffer into the buffer,
+ * Efficiently copy buffer from a source IO::Buffer into the buffer,
* at +offset+ using +memcpy+. For copying String instances, see #set_string.
*
* buffer = IO::Buffer.new(32)
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
*
* buffer.copy(IO::Buffer.for("test"), 8)
- * # => 4 -- size of data copied
+ * # => 4 -- size of buffer copied
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
* # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
*
- * #copy can be used to put data into strings associated with buffer:
+ * #copy can be used to put buffer into strings associated with buffer:
*
- * string= "data: "
- * # => "data: "
+ * string= "buffer: "
+ * # => "buffer: "
* buffer = IO::Buffer.for(string)
* buffer.copy(IO::Buffer.for("test"), 5)
* # => 4
* string
- * # => "data:test"
+ * # => "buffer:test"
*
* Attempt to copy into a read-only buffer will fail:
*
@@ -1784,21 +2263,20 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* File.read('test.txt')
* # => "boom"
*
- * Attempt to copy the data which will need place outside of buffer's
+ * Attempt to copy the buffer which will need place outside of buffer's
* bounds will fail:
*
* buffer = IO::Buffer.new(2)
* buffer.copy(IO::Buffer.for('test'), 0)
- * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
- *
+ * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
{
- if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
+ rb_check_arity(argc, 1, 4);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE source = argv[0];
const void *source_base;
@@ -1806,7 +2284,7 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
+ return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
}
/*
@@ -1815,47 +2293,35 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self)
* Read a chunk or all of the buffer into a string, in the specified
* +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
*
- * buffer = IO::Buffer.for('test')
- * buffer.get_string
- * # => "test"
- * buffer.get_string(2)
- * # => "st"
- * buffer.get_string(2, 1)
- * # => "s"
- *
+ * buffer = IO::Buffer.for('test')
+ * buffer.get_string
+ * # => "test"
+ * buffer.get_string(2)
+ * # => "st"
+ * buffer.get_string(2, 1)
+ * # => "s"
*/
static VALUE
io_buffer_get_string(int argc, VALUE *argv, VALUE self)
{
- if (argc > 3) rb_error_arity(argc, 0, 3);
+ rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
const void *base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
-
- size_t offset = 0;
- size_t length = size;
- rb_encoding *encoding = rb_ascii8bit_encoding();
-
- if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
- }
- else {
- length = size - offset;
- }
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+ rb_encoding *encoding;
if (argc >= 3) {
encoding = rb_find_encoding(argv[2]);
}
+ else {
+ encoding = rb_ascii8bit_encoding();
+ }
- io_buffer_validate_range(data, offset, length);
+ io_buffer_validate_range(buffer, offset, length);
return rb_enc_str_new((const char*)base + offset, length, encoding);
}
@@ -1863,7 +2329,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
/*
* call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy data from a source String into the buffer,
+ * Efficiently copy buffer from a source String into the buffer,
* at +offset+ using +memcpy+.
*
* buf = IO::Buffer.new(8)
@@ -1871,7 +2337,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
* # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00 ........
*
- * # set data starting from offset 1, take 2 bytes starting from string's
+ * # set buffer starting from offset 1, take 2 bytes starting from string's
* # second
* buf.set_string('test', 1, 2, 1)
* # => 2
@@ -1886,30 +2352,30 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
static VALUE
io_buffer_set_string(int argc, VALUE *argv, VALUE self)
{
- if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
+ rb_check_arity(argc, 1, 4);
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
VALUE string = rb_str_to_str(argv[0]);
const void *source_base = RSTRING_PTR(string);
size_t source_size = RSTRING_LEN(string);
- return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
+ return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
}
void
rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
void *base;
size_t size;
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
-
- if (offset + length > size) {
- rb_raise(rb_eArgError, "The given offset + length out of bounds!");
- }
+ io_buffer_validate_range(buffer, offset, length);
memset((char*)base + offset, value, length);
}
@@ -1944,33 +2410,19 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
* # =>
* # <IO::Buffer 0x00007fca40087c38+4 SLICE>
* # 0x00000000 01 02 02 02 ....
- *
*/
static VALUE
io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
- if (argc > 3) rb_error_arity(argc, 0, 3);
-
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ rb_check_arity(argc, 0, 3);
uint8_t value = 0;
if (argc >= 1) {
value = NUM2UINT(argv[0]);
}
- size_t offset = 0;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
-
- size_t length;
- if (argc >= 3) {
- length = NUM2SIZET(argv[2]);
- }
- else {
- length = data->size - offset;
- }
+ size_t offset, length;
+ io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
rb_io_buffer_clear(self, value, offset, length);
@@ -2002,170 +2454,486 @@ io_buffer_default_size(size_t page_size)
return platform_agnostic_default_size;
}
-VALUE
-rb_io_buffer_read(VALUE self, VALUE io, size_t length)
+struct io_buffer_blocking_region_argument {
+ struct rb_io_buffer *buffer;
+ rb_blocking_function_t *function;
+ void *data;
+ int descriptor;
+};
+
+static VALUE
+io_buffer_blocking_region_begin(VALUE _argument)
{
- VALUE scheduler = rb_fiber_scheduler_current();
- if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length);
+ struct io_buffer_blocking_region_argument *argument = (void*)_argument;
- if (result != Qundef) {
- return result;
- }
- }
+ return rb_thread_io_blocking_region(argument->function, argument->data, argument->descriptor);
+}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+static VALUE
+io_buffer_blocking_region_ensure(VALUE _argument)
+{
+ struct io_buffer_blocking_region_argument *argument = (void*)_argument;
- io_buffer_validate_range(data, 0, length);
+ io_buffer_unlock(argument->buffer);
- int descriptor = rb_io_descriptor(io);
+ return Qnil;
+}
- void * base;
- size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+static VALUE
+io_buffer_blocking_region(struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data, int descriptor)
+{
+ struct io_buffer_blocking_region_argument argument = {
+ .buffer = buffer,
+ .function = function,
+ .data = data,
+ .descriptor = descriptor,
+ };
- ssize_t result = read(descriptor, base, size);
+ // If the buffer is already locked, we can skip the ensure (unlock):
+ if (buffer->flags & RB_IO_BUFFER_LOCKED) {
+ return io_buffer_blocking_region_begin((VALUE)&argument);
+ }
+ else {
+ // The buffer should be locked for the duration of the blocking region:
+ io_buffer_lock(buffer);
- return rb_fiber_scheduler_io_result(result, errno);
+ return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
+ }
}
+struct io_buffer_read_internal_argument {
+ int descriptor;
+
+ // The base pointer to read from:
+ char *base;
+ // The size of the buffer:
+ size_t size;
+
+ // The minimum number of bytes to read:
+ size_t length;
+};
+
static VALUE
-io_buffer_read(VALUE self, VALUE io, VALUE length)
+io_buffer_read_internal(void *_argument)
{
- return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
+ size_t total = 0;
+ struct io_buffer_read_internal_argument *argument = _argument;
+
+ while (true) {
+ ssize_t result = read(argument->descriptor, argument->base, argument->size);
+
+ if (result < 0) {
+ return rb_fiber_scheduler_io_result(result, errno);
+ }
+ else if (result == 0) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+ else {
+ total += result;
+
+ if (total >= argument->length) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+
+ argument->base = argument->base + result;
+ argument->size = argument->size - result;
+ }
+ }
}
VALUE
-rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset)
+rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset);
+ VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
void * base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
+
+ base = (unsigned char*)base + offset;
+ size = size - offset;
+
+ struct io_buffer_read_internal_argument argument = {
+ .descriptor = descriptor,
+ .base = base,
+ .size = size,
+ .length = length,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_read_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: read(io, [length, [offset]]) -> read length or -errno
+ *
+ * Read at least +length+ bytes from the +io+, into the buffer starting at
+ * +offset+. If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +length+ is not given or +nil+, it defaults to the size of the buffer
+ * minus the offset, i.e. the entire buffer.
+ *
+ * If +length+ is zero, exactly one <tt>read</tt> operation will occur.
+ *
+ * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
+ * buffer.
+ *
+ * IO::Buffer.for('test') do |buffer|
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ * buffer.read(File.open('/dev/urandom', 'rb'), 2)
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
+ * # 0x00000000 05 35 73 74 .5st
+ * end
+ */
+static VALUE
+io_buffer_read(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 3);
+
+ VALUE io = argv[0];
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
+
+ return rb_io_buffer_read(self, io, length, offset);
+}
+
+struct io_buffer_pread_internal_argument {
+ int descriptor;
+ void *base;
+ size_t size;
+ off_t offset;
+};
+
+static VALUE
+io_buffer_pread_internal(void *_argument)
+{
+ struct io_buffer_pread_internal_argument *argument = _argument;
#if defined(HAVE_PREAD)
- ssize_t result = pread(descriptor, base, size, offset);
+ ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset);
#else
- // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
- off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
- if (current_offset == (off_t)-1)
+ // This emulation is not thread safe.
+ rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR);
+ if (offset == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- ssize_t result = read(descriptor, base, size);
+ ssize_t result = read(argument->descriptor, argument->base, argument->size);
- if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
#endif
return rb_fiber_scheduler_io_result(result, errno);
}
-static VALUE
-io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
-{
- return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
-}
-
VALUE
-rb_io_buffer_write(VALUE self, VALUE io, size_t length)
+rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length);
+ VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
- const void * base;
+ void * base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- ssize_t result = write(descriptor, base, length);
+ struct io_buffer_pread_internal_argument argument = {
+ .descriptor = descriptor,
- return rb_fiber_scheduler_io_result(result, errno);
+ // Move the base pointer to the offset:
+ .base = (unsigned char*)base + offset,
+
+ // And the size to the length of buffer we want to read:
+ .size = length,
+
+ // From the offset in the file we want to read from:
+ .offset = from,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_pread_internal, &argument, descriptor);
}
+/*
+ * call-seq: pread(io, from, length, [offset]) -> read length or -errno
+ *
+ * Read at most +length+ bytes from +io+ into the buffer, starting at
+ * +from+, and put it in buffer starting from specified +offset+.
+ * If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +offset+ is not given, put it at the beginning of the buffer.
+ *
+ * Example:
+ *
+ * IO::Buffer.for('test') do |buffer|
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # 0x00000000 74 65 73 74 test
+ *
+ * # take 2 bytes from the beginning of urandom,
+ * # put them in buffer starting from position 2
+ * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
+ * p buffer
+ * # =>
+ * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
+ * # 0x00000000 05 35 73 74 te.5
+ * end
+ */
static VALUE
-io_buffer_write(VALUE self, VALUE io, VALUE length)
+io_buffer_pread(int argc, VALUE *argv, VALUE self)
{
- return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
+ rb_check_arity(argc, 2, 4);
+
+ VALUE io = argv[0];
+ rb_off_t from = NUM2OFFT(argv[1]);
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
+
+ return rb_io_buffer_pread(self, io, from, length, offset);
+}
+
+struct io_buffer_write_internal_argument {
+ int descriptor;
+
+ // The base pointer to write from:
+ const char *base;
+ // The size of the buffer:
+ size_t size;
+
+ // The minimum length to write:
+ size_t length;
+};
+
+static VALUE
+io_buffer_write_internal(void *_argument)
+{
+ size_t total = 0;
+ struct io_buffer_write_internal_argument *argument = _argument;
+
+ while (true) {
+ ssize_t result = write(argument->descriptor, argument->base, argument->size);
+
+ if (result < 0) {
+ return rb_fiber_scheduler_io_result(result, errno);
+ }
+ else if (result == 0) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+ else {
+ total += result;
+
+ if (total >= argument->length) {
+ return rb_fiber_scheduler_io_result(total, 0);
+ }
+
+ argument->base = argument->base + result;
+ argument->size = argument->size - result;
+ }
+ }
}
VALUE
-rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset)
+rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
- VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset));
+ VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length, offset);
- if (result != Qundef) {
+ if (!UNDEF_P(result)) {
return result;
}
}
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_validate_range(data, 0, length);
+ io_buffer_validate_range(buffer, offset, length);
int descriptor = rb_io_descriptor(io);
const void * base;
size_t size;
- io_buffer_get_bytes_for_reading(data, &base, &size);
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ base = (unsigned char*)base + offset;
+ size = size - offset;
+
+ struct io_buffer_write_internal_argument argument = {
+ .descriptor = descriptor,
+ .base = base,
+ .size = size,
+ .length = length,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_write_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: write(io, [length, [offset]]) -> written length or -errno
+ *
+ * Write at least +length+ bytes from the buffer starting at +offset+, into the +io+.
+ * If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +length+ is not given or +nil+, it defaults to the size of the buffer
+ * minus the offset, i.e. the entire buffer.
+ *
+ * If +length+ is zero, exactly one <tt>write</tt> operation will occur.
+ *
+ * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
+ * buffer.
+ *
+ * out = File.open('output.txt', 'wb')
+ * IO::Buffer.for('1234567').write(out, 3)
+ *
+ * This leads to +123+ being written into <tt>output.txt</tt>
+ */
+static VALUE
+io_buffer_write(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 3);
+
+ VALUE io = argv[0];
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
+
+ return rb_io_buffer_write(self, io, length, offset);
+}
+
+struct io_buffer_pwrite_internal_argument {
+ int descriptor;
+ const void *base;
+ size_t size;
+ off_t offset;
+};
+
+static VALUE
+io_buffer_pwrite_internal(void *_argument)
+{
+ struct io_buffer_pwrite_internal_argument *argument = _argument;
#if defined(HAVE_PWRITE)
- ssize_t result = pwrite(descriptor, base, length, offset);
+ ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset);
#else
- // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
- off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
- if (current_offset == (off_t)-1)
+ // This emulation is not thread safe.
+ rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR);
+ if (offset == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
- ssize_t result = write(descriptor, base, length);
+ ssize_t result = write(argument->descriptor, argument->base, argument->size);
- if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
+ if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1)
return rb_fiber_scheduler_io_result(-1, errno);
#endif
return rb_fiber_scheduler_io_result(result, errno);
}
+VALUE
+rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
+{
+ VALUE scheduler = rb_fiber_scheduler_current();
+ if (scheduler != Qnil) {
+ VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, length, offset);
+
+ if (!UNDEF_P(result)) {
+ return result;
+ }
+ }
+
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_validate_range(buffer, offset, length);
+
+ int descriptor = rb_io_descriptor(io);
+
+ const void * base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ struct io_buffer_pwrite_internal_argument argument = {
+ .descriptor = descriptor,
+
+ // Move the base pointer to the offset:
+ .base = (unsigned char *)base + offset,
+
+ // And the size to the length of buffer we want to read:
+ .size = length,
+
+ // And the offset in the file we want to write from:
+ .offset = from,
+ };
+
+ return io_buffer_blocking_region(buffer, io_buffer_pwrite_internal, &argument, descriptor);
+}
+
+/*
+ * call-seq: pwrite(io, from, length, [offset]) -> written length or -errno
+ *
+ * Writes +length+ bytes from buffer into +io+, starting at
+ * +offset+ in the buffer. If an error occurs, return <tt>-errno</tt>.
+ *
+ * If +offset+ is not given, the bytes are taken from the beginning of the
+ * buffer. If the +offset+ is given and is beyond the end of the file, the
+ * gap will be filled with null (0 value) bytes.
+ *
+ * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
+ * IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
+ *
+ * This leads to +234+ (3 bytes, starting from position 1) being written into
+ * <tt>output.txt</tt>, starting from file position 2.
+ */
static VALUE
-io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
+io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
{
- return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
+ rb_check_arity(argc, 2, 4);
+
+ VALUE io = argv[0];
+ rb_off_t from = NUM2OFFT(argv[1]);
+
+ size_t length, offset;
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
+
+ return rb_io_buffer_pwrite(self, io, from, length, offset);
}
static inline void
@@ -2198,19 +2966,19 @@ memory_and(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_and(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_and(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_and(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2238,19 +3006,19 @@ memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t
static VALUE
io_buffer_or(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_or(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_or(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2278,19 +3046,19 @@ memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_xor(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
+ io_buffer_check_mask(mask_buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_xor(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
+ memory_xor(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
return output;
}
@@ -2318,14 +3086,14 @@ memory_not(unsigned char * restrict output, unsigned char * restrict base, size_
static VALUE
io_buffer_not(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
- struct rb_io_buffer *output_data = NULL;
- TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
+ VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ struct rb_io_buffer *output_buffer = NULL;
+ TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_not(output_data->base, data->base, data->size);
+ memory_not(output_buffer->base, buffer->base, buffer->size);
return output;
}
@@ -2344,7 +3112,7 @@ static inline void
io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
{
if (io_buffer_overlaps(a, b))
- rb_raise(rb_eIOBufferMaskError, "Mask overlaps source data!");
+ rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
}
static void
@@ -2375,20 +3143,20 @@ memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * r
static VALUE
io_buffer_and_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_and_inplace(base, size, mask_data->base, mask_data->size);
+ memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2421,20 +3189,20 @@ memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * re
static VALUE
io_buffer_or_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_or_inplace(base, size, mask_data->base, mask_data->size);
+ memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2467,20 +3235,20 @@ memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * r
static VALUE
io_buffer_xor_inplace(VALUE self, VALUE mask)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- struct rb_io_buffer *mask_data = NULL;
- TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
+ struct rb_io_buffer *mask_buffer = NULL;
+ TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_data);
- io_buffer_check_overlaps(data, mask_data);
+ io_buffer_check_mask(mask_buffer);
+ io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- memory_xor_inplace(base, size, mask_data->base, mask_data->size);
+ memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
return self;
}
@@ -2513,12 +3281,12 @@ memory_not_inplace(unsigned char * restrict base, size_t size)
static VALUE
io_buffer_not_inplace(VALUE self)
{
- struct rb_io_buffer *data = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
void *base;
size_t size;
- io_buffer_get_bytes_for_writing(data, &base, &size);
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
memory_not_inplace(base, size);
@@ -2531,8 +3299,8 @@ io_buffer_not_inplace(VALUE self)
* IO::Buffer is a low-level efficient buffer for input/output. There are three
* ways of using buffer:
*
- * * Create an empty buffer with ::new, fill it with data using #copy or
- * #set_value, #set_string, get data with #get_string;
+ * * Create an empty buffer with ::new, fill it with buffer using #copy or
+ * #set_value, #set_string, get buffer with #get_string;
* * Create a buffer mapped to some string with ::for, then it could be used
* both for reading with #get_string or #get_value, and writing (writing will
* change the source string, too);
@@ -2550,11 +3318,11 @@ io_buffer_not_inplace(VALUE self)
* Empty buffer:
*
* buffer = IO::Buffer.new(8) # create empty 8-byte buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
* # ...
* buffer
- * # =>
+ * # =>
* # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
* # 0x00000000 00 00 00 00 00 00 00 00
* buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
@@ -2564,22 +3332,22 @@ io_buffer_not_inplace(VALUE self)
*
* \Buffer from string:
*
- * string = 'data'
+ * string = 'buffer'
* buffer = IO::Buffer.for(string)
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
* # ...
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
- * # 0x00000000 64 61 74 61 data
+ * # 0x00000000 64 61 74 61 buffer
*
* buffer.get_string(2) # read content starting from offset 2
* # => "ta"
* buffer.set_string('---', 1) # write content, starting from offset 1
* # => 3
* buffer
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
* # 0x00000000 64 2d 2d 2d d---
* string # original string changed, too
@@ -2587,10 +3355,10 @@ io_buffer_not_inplace(VALUE self)
*
* \Buffer from file:
*
- * File.write('test.txt', 'test data')
+ * File.write('test.txt', 'test buffer')
* # => 9
* buffer = IO::Buffer.map(File.open('test.txt'))
- * # =>
+ * # =>
* # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
* # ...
* buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
@@ -2604,7 +3372,7 @@ io_buffer_not_inplace(VALUE self)
* buffer.set_string('---', 1)
* # => 3 -- bytes written
* File.read('test.txt')
- * # => "t--- data"
+ * # => "t--- buffer"
*
* <b>The class is experimental and the interface is subject to change.</b>
*/
@@ -2653,6 +3421,7 @@ Init_IO_Buffer(void)
rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
+ rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
@@ -2668,6 +3437,7 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
+ rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
@@ -2677,7 +3447,7 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
// Manipulation:
- rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2);
+ rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
@@ -2685,24 +3455,48 @@ Init_IO_Buffer(void)
rb_include_module(rb_cIOBuffer, rb_mComparable);
-#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name)
- DEFINE_TYPE(U8); DEFINE_TYPE(S8);
- DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16);
- DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32);
- DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64);
- DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64);
-#undef DEFINE_TYPE
+#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
+ IO_BUFFER_DEFINE_DATA_TYPE(U8);
+ IO_BUFFER_DEFINE_DATA_TYPE(S8);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u16);
+ IO_BUFFER_DEFINE_DATA_TYPE(U16);
+ IO_BUFFER_DEFINE_DATA_TYPE(s16);
+ IO_BUFFER_DEFINE_DATA_TYPE(S16);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u32);
+ IO_BUFFER_DEFINE_DATA_TYPE(U32);
+ IO_BUFFER_DEFINE_DATA_TYPE(s32);
+ IO_BUFFER_DEFINE_DATA_TYPE(S32);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(u64);
+ IO_BUFFER_DEFINE_DATA_TYPE(U64);
+ IO_BUFFER_DEFINE_DATA_TYPE(s64);
+ IO_BUFFER_DEFINE_DATA_TYPE(S64);
+
+ IO_BUFFER_DEFINE_DATA_TYPE(f32);
+ IO_BUFFER_DEFINE_DATA_TYPE(F32);
+ IO_BUFFER_DEFINE_DATA_TYPE(f64);
+ IO_BUFFER_DEFINE_DATA_TYPE(F64);
+#undef IO_BUFFER_DEFINE_DATA_TYPE
+
+ rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
// Data access:
rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
+ rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
+ rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
+ rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
+ rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
+ rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
- // Binary data manipulations:
+ // Binary buffer manipulations:
rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
@@ -2714,8 +3508,8 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
// IO operations:
- rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
- rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
- rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2);
- rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3);
+ rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
+ rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
+ rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
+ rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
}
diff --git a/iseq.c b/iseq.c
index f17a2d49b6..d17ce486c5 100644
--- a/iseq.c
+++ b/iseq.c
@@ -102,68 +102,60 @@ compile_data_free(struct iseq_compile_data *compile_data)
}
}
-struct iseq_clear_ic_references_data {
- IC ic;
-};
-
-// This iterator is used to walk through the instructions and clean any
-// references to ICs that are contained within this ISEQ out of the VM's
-// constant cache table. It passes around a struct that holds the current IC
-// we're looking for, which can be NULL (if we haven't hit an opt_getinlinecache
-// instruction yet) or set to an IC (if we've hit an opt_getinlinecache and
-// haven't yet hit the associated opt_setinlinecache).
-static bool
-iseq_clear_ic_references_i(VALUE *code, VALUE insn, size_t index, void *data)
+static void
+remove_from_constant_cache(ID id, IC ic)
{
- struct iseq_clear_ic_references_data *ic_data = (struct iseq_clear_ic_references_data *) data;
+ rb_vm_t *vm = GET_VM();
+ VALUE lookup_result;
+ st_data_t ic_data = (st_data_t)ic;
- switch (insn) {
- case BIN(opt_getinlinecache): {
- RUBY_ASSERT_ALWAYS(ic_data->ic == NULL);
+ if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
+ st_table *ics = (st_table *)lookup_result;
+ st_delete(ics, &ic_data, NULL);
- ic_data->ic = (IC) code[index + 2];
- return true;
- }
- case BIN(getconstant): {
- if (ic_data->ic != NULL) {
- ID id = (ID) code[index + 1];
- rb_vm_t *vm = GET_VM();
- VALUE lookup_result;
-
- if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
- st_table *ics = (st_table *)lookup_result;
- st_data_t ic = (st_data_t)ic_data->ic;
- st_delete(ics, &ic, NULL);
-
- if (ics->num_entries == 0) {
- rb_id_table_delete(vm->constant_cache, id);
- st_free_table(ics);
- }
- }
+ if (ics->num_entries == 0 &&
+ // See comment in vm_track_constant_cache on why we need this check
+ id != vm->inserting_constant_cache_id) {
+ rb_id_table_delete(vm->constant_cache, id);
+ st_free_table(ics);
}
-
- return true;
- }
- case BIN(opt_setinlinecache): {
- RUBY_ASSERT_ALWAYS(ic_data->ic != NULL);
-
- ic_data->ic = NULL;
- return true;
- }
- default:
- return true;
}
}
// When an ISEQ is being freed, all of its associated ICs are going to go away
-// as well. Because of this, we need to walk through the ISEQ, find any
-// opt_getinlinecache calls, and clear out the VM's constant cache of associated
-// ICs.
+// as well. Because of this, we need to iterate over the ICs, and clear them
+// from the VM's constant cache.
static void
iseq_clear_ic_references(const rb_iseq_t *iseq)
{
- struct iseq_clear_ic_references_data data = { .ic = NULL };
- rb_iseq_each(iseq, 0, iseq_clear_ic_references_i, (void *) &data);
+ // In some cases (when there is a compilation error), we end up with
+ // ic_size greater than 0, but no allocated is_entries buffer.
+ // If there's no is_entries buffer to loop through, return early.
+ // [Bug #19173]
+ if (!ISEQ_BODY(iseq)->is_entries) {
+ return;
+ }
+
+ for (unsigned int ic_idx = 0; ic_idx < ISEQ_BODY(iseq)->ic_size; ic_idx++) {
+ IC ic = &ISEQ_IS_IC_ENTRY(ISEQ_BODY(iseq), ic_idx);
+
+ // Iterate over the IC's constant path's segments and clean any references to
+ // the ICs out of the VM's constant cache table.
+ const ID *segments = ic->segments;
+
+ // It's possible that segments is NULL if we overallocated an IC but
+ // optimizations removed the instruction using it
+ if (segments == NULL)
+ continue;
+
+ for (int i = 0; segments[i]; i++) {
+ ID id = segments[i];
+ if (id == idNULL) continue;
+ remove_from_constant_cache(id, ic);
+ }
+
+ ruby_xfree((void *)segments);
+ }
}
void
@@ -197,7 +189,11 @@ rb_iseq_free(const rb_iseq_t *iseq)
ruby_xfree((void *)body->mark_bits.list);
}
+ ruby_xfree(body->variable.original_iseq);
+
if (body->param.keyword != NULL) {
+ if (body->param.keyword->table != &body->local_table[body->param.keyword->bits_start - body->param.keyword->num])
+ ruby_xfree((void *)body->param.keyword->table);
ruby_xfree((void *)body->param.keyword->default_values);
ruby_xfree((void *)body->param.keyword);
}
@@ -213,32 +209,7 @@ rb_iseq_free(const rb_iseq_t *iseq)
RUBY_FREE_LEAVE("iseq");
}
-#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
-static VALUE
-rb_vm_insn_addr2insn2(const void *addr)
-{
- return (VALUE)rb_vm_insn_addr2insn(addr);
-}
-#endif
-
-// The translator for OPT_DIRECT_THREADED_CODE and OPT_CALL_THREADED_CODE does
-// some normalization to always return the non-trace version of instructions. To
-// mirror that behavior in token-threaded environments, we normalize in this
-// translator by also returning non-trace opcodes.
-static VALUE
-rb_vm_insn_normalizing_translator(const void *addr)
-{
- VALUE opcode = (VALUE)addr;
- VALUE trace_opcode_threshold = (VM_INSTRUCTION_SIZE / 2);
-
- if (opcode >= trace_opcode_threshold) {
- return opcode - trace_opcode_threshold;
- }
- return opcode;
-}
-
typedef VALUE iseq_value_itr_t(void *ctx, VALUE obj);
-typedef VALUE rb_vm_insns_translator_t(const void *addr);
static inline void
iseq_scan_bits(unsigned int page, iseq_bits_t bits, VALUE *code, iseq_value_itr_t *func, void *data)
@@ -274,18 +245,8 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
union iseq_inline_storage_entry *is_entries = body->is_entries;
if (body->is_entries) {
- // IVC entries
- for (unsigned int i = 0; i < body->ivc_size; i++, is_entries++) {
- IVC ivc = (IVC)is_entries;
- if (ivc->entry) {
- RUBY_ASSERT(!RB_TYPE_P(ivc->entry->class_value, T_NONE));
-
- VALUE nv = func(data, ivc->entry->class_value);
- if (ivc->entry->class_value != nv) {
- ivc->entry->class_value = nv;
- }
- }
- }
+ // Skip iterating over ivc caches
+ is_entries += body->ivc_size;
// ICVARC entries
for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) {
@@ -339,39 +300,6 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
}
}
-// Similar to rb_iseq_each_value, except that this walks through each
-// instruction instead of the associated VALUEs. The provided iterator should
-// return a boolean that indicates whether or not to continue iterating.
-void
-rb_iseq_each(const rb_iseq_t *iseq, size_t start_index, rb_iseq_each_i iterator, void *data)
-{
- unsigned int size;
- VALUE *code;
- size_t index;
-
- rb_vm_insns_translator_t *const translator =
-#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
- (FL_TEST((VALUE)iseq, ISEQ_TRANSLATED)) ? rb_vm_insn_addr2insn2 :
-#endif
- rb_vm_insn_normalizing_translator; // Always pass non-trace opcodes.
-
- const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
-
- size = body->iseq_size;
- code = body->iseq_encoded;
-
- for (index = start_index; index < size;) {
- void *addr = (void *) code[index];
- VALUE insn = translator(addr);
-
- if (!iterator(code, insn, index, data)) {
- break;
- }
-
- index += insn_len(insn);
- }
-}
-
static VALUE
update_each_insn_value(void *ctx, VALUE obj)
{
@@ -418,7 +346,7 @@ rb_iseq_update_references(rb_iseq_t *iseq)
for (j = 0; i < body->param.keyword->num; i++, j++) {
VALUE obj = body->param.keyword->default_values[j];
- if (obj != Qundef) {
+ if (!UNDEF_P(obj)) {
body->param.keyword->default_values[j] = rb_gc_location(obj);
}
}
@@ -593,6 +521,19 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
/* body->is_entries */
size += ISEQ_IS_SIZE(body) * sizeof(union iseq_inline_storage_entry);
+ if (ISEQ_BODY(iseq)->is_entries) {
+ /* IC entries constant segments */
+ for (unsigned int ic_idx = 0; ic_idx < body->ic_size; ic_idx++) {
+ IC ic = &ISEQ_IS_IC_ENTRY(body, ic_idx);
+ const ID *ids = ic->segments;
+ if (!ids) continue;
+ while (*ids++) {
+ size += sizeof(ID);
+ }
+ size += sizeof(ID); // null terminator
+ }
+ }
+
/* body->call_data */
size += body->ci_size * sizeof(struct rb_call_data);
// TODO: should we count imemo_callinfo?
@@ -656,8 +597,21 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
rb_iseq_pathobj_new(path, realpath));
}
+// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
+rb_iseq_t *
+rb_iseq_alloc_with_dummy_path(VALUE fname)
+{
+ rb_iseq_t *dummy_iseq = iseq_alloc();
+
+ ISEQ_BODY(dummy_iseq)->type = ISEQ_TYPE_TOP;
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.pathobj, fname);
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.label, fname);
+
+ return dummy_iseq;
+}
+
static rb_iseq_location_t *
-iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id)
+iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
{
rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
@@ -722,7 +676,7 @@ new_arena(void)
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
- VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id,
+ VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id,
const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type,
VALUE script_lines, const rb_compile_option_t *option)
{
@@ -763,7 +717,6 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
-
if (option->coverage_enabled) {
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
@@ -949,7 +902,7 @@ rb_iseq_t *
rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
const rb_iseq_t *parent, enum rb_iseq_type type)
{
- return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent,
+ return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent,
0, type, &COMPILE_OPTION_DEFAULT);
}
@@ -966,34 +919,62 @@ ast_line_count(const rb_ast_body_t *ast)
return FIX2INT(ast->script_lines);
}
-rb_iseq_t *
-rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+static VALUE
+iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset)
+{
+ int line_count = line_offset + ast_line_count(ast);
+
+ if (line_count >= 0) {
+ int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
+
+ VALUE coverage = rb_default_coverage(len);
+ rb_hash_aset(coverages, path, coverage);
+
+ return coverage;
+ }
+
+ return Qnil;
+}
+
+static inline void
+iseq_new_setup_coverage(VALUE path, const rb_ast_body_t *ast, int line_offset)
{
VALUE coverages = rb_get_coverages();
+
if (RTEST(coverages)) {
- int line_count = ast_line_count(ast);
- if (line_count >= 0) {
- int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
- VALUE coverage = rb_default_coverage(len);
- rb_hash_aset(coverages, path, coverage);
- }
+ iseq_setup_coverage(coverages, path, ast, 0);
}
+}
- return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0,
+rb_iseq_t *
+rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+{
+ iseq_new_setup_coverage(path, ast, 0);
+
+ return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0,
ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT);
}
rb_iseq_t *
rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
{
+ iseq_new_setup_coverage(path, ast, 0);
+
return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"),
- path, realpath, INT2FIX(0),
+ path, realpath, 0,
parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE);
}
rb_iseq_t *
-rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth)
+rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
{
+ if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
+ iseq_setup_coverage(coverages, path, ast, first_lineno - 1);
+ }
+ }
+
return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno,
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT);
}
@@ -1014,7 +995,7 @@ iseq_translate(rb_iseq_t *iseq)
rb_iseq_t *
rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
- VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth,
+ int first_lineno, const rb_iseq_t *parent, int isolated_depth,
enum rb_iseq_type type, const rb_compile_option_t *option)
{
const NODE *node = ast ? ast->root : 0;
@@ -1052,7 +1033,7 @@ rb_iseq_t *
rb_iseq_new_with_callback(
const struct rb_iseq_new_with_callback_callback_func * ifunc,
VALUE name, VALUE path, VALUE realpath,
- VALUE first_lineno, const rb_iseq_t *parent,
+ int first_lineno, const rb_iseq_t *parent,
enum rb_iseq_type type, const rb_compile_option_t *option)
{
/* TODO: argument check */
@@ -1118,7 +1099,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
rb_iseq_t *iseq = iseq_alloc();
VALUE magic, version1, version2, format_type, misc;
- VALUE name, path, realpath, first_lineno, code_location, node_id;
+ VALUE name, path, realpath, code_location, node_id;
VALUE type, body, locals, params, exception;
st_data_t iseq_type;
@@ -1144,7 +1125,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
path = CHECK_STRING(rb_ary_entry(data, i++));
realpath = rb_ary_entry(data, i++);
realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath);
- first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++));
+ int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++));
type = CHECK_SYMBOL(rb_ary_entry(data, i++));
locals = CHECK_ARRAY(rb_ary_entry(data, i++));
@@ -1238,7 +1219,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
rb_exc_raise(GET_EC()->errinfo);
}
else {
- iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, line,
+ iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln,
NULL, 0, ISEQ_TYPE_TOP, &option);
rb_ast_dispose(ast);
}
@@ -1285,7 +1266,7 @@ rb_iseq_base_label(const rb_iseq_t *iseq)
VALUE
rb_iseq_first_lineno(const rb_iseq_t *iseq)
{
- return ISEQ_BODY(iseq)->location.first_lineno;
+ return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
}
VALUE
@@ -1477,7 +1458,7 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self)
static VALUE
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
{
- VALUE file, line = INT2FIX(1), opt = Qnil;
+ VALUE file, opt = Qnil;
VALUE parser, f, exc = Qnil, ret;
rb_ast_t *ast;
rb_compile_option_t option;
@@ -1493,6 +1474,9 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
f = rb_file_open_str(file, "r");
+ rb_execution_context_t *ec = GET_EC();
+ VALUE v = rb_vm_push_frame_fname(ec, file);
+
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
ast = (rb_ast_t *)rb_parser_load_file(parser, file);
@@ -1509,8 +1493,11 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"),
file,
rb_realpath_internal(Qnil, file, 1),
- line, NULL, 0, ISEQ_TYPE_TOP, &option));
+ 1, NULL, 0, ISEQ_TYPE_TOP, &option));
rb_ast_dispose(ast);
+
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
return ret;
}
@@ -1600,7 +1587,11 @@ rb_iseqw_to_iseq(VALUE iseqw)
static VALUE
iseqw_eval(VALUE self)
{
- return rb_iseq_eval(iseqw_check(self));
+ const rb_iseq_t *iseq = iseqw_check(self);
+ if (0 == ISEQ_BODY(iseq)->iseq_size) {
+ rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence");
+ }
+ return rb_iseq_eval(iseq);
}
/*
@@ -2175,6 +2166,16 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
}
case TS_IC:
+ {
+ ret = rb_sprintf("<ic:%"PRIdPTRDIFF" ", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
+ const ID *segments = ((IC)op)->segments;
+ rb_str_cat2(ret, rb_id2name(*segments++));
+ while (*segments) {
+ rb_str_catf(ret, "::%s", rb_id2name(*segments++));
+ }
+ rb_str_cat2(ret, ">");
+ }
+ break;
case TS_IVC:
case TS_ICVARC:
case TS_ISE:
@@ -2411,7 +2412,7 @@ rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
rb_str_cat2(str, "== disasm: ");
rb_str_append(str, iseq_inspect(iseq));
- rb_str_catf(str, " (catch: %s)", body->catch_except_p ? "TRUE" : "FALSE");
+ rb_str_catf(str, " (catch: %s)", body->catch_except_p ? "true" : "false");
if ((l = RSTRING_LEN(str) - indent_len) < header_minlen) {
rb_str_modify_expand(str, header_minlen - l);
memset(RSTRING_END(str), '=', header_minlen - l);
@@ -2533,6 +2534,34 @@ rb_iseq_disasm(const rb_iseq_t *iseq)
}
/*
+ * Estimates the number of instance variables that will be set on
+ * a given `class` with the initialize method defined in
+ * `initialize_iseq`
+ */
+attr_index_t
+rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq)
+{
+ struct rb_id_table * iv_names = rb_id_table_create(0);
+
+ for (unsigned int i = 0; i < ISEQ_BODY(initialize_iseq)->ivc_size; i++) {
+ IVC cache = (IVC)&ISEQ_BODY(initialize_iseq)->is_entries[i];
+
+ if (cache->iv_set_name) {
+ rb_id_table_insert(iv_names, cache->iv_set_name, Qtrue);
+ }
+ }
+
+ attr_index_t count = (attr_index_t)rb_id_table_size(iv_names);
+
+ VALUE superclass = rb_class_superclass(klass);
+ count += RCLASS_EXT(superclass)->max_iv_count;
+
+ rb_id_table_free(iv_names);
+
+ return count;
+}
+
+/*
* call-seq:
* iseq.disasm -> str
* iseq.disassemble -> str
@@ -2958,7 +2987,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
}
for (j=0; i<keyword->num; i++, j++) {
VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i]));
- if (keyword->default_values[j] != Qundef) {
+ if (!UNDEF_P(keyword->default_values[j])) {
rb_ary_push(key, keyword->default_values[j]);
}
rb_ary_push(keywords, key);
@@ -3011,6 +3040,15 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
}
break;
case TS_IC:
+ {
+ VALUE list = rb_ary_new();
+ const ID *ids = ((IC)*seq)->segments;
+ while (*ids) {
+ rb_ary_push(list, ID2SYM(*ids++));
+ }
+ rb_ary_push(ary, list);
+ }
+ break;
case TS_IVC:
case TS_ICVARC:
case TS_ISE:
@@ -3195,7 +3233,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_ary_push(val, iseq_body->location.label);
rb_ary_push(val, rb_iseq_path(iseq));
rb_ary_push(val, rb_iseq_realpath(iseq));
- rb_ary_push(val, iseq_body->location.first_lineno);
+ rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno));
rb_ary_push(val, ID2SYM(type));
rb_ary_push(val, locals);
rb_ary_push(val, params);
@@ -3352,7 +3390,7 @@ rb_vm_encoded_insn_data_table_init(void)
const void * const *table = rb_vm_get_insns_address_table();
#define INSN_CODE(insn) ((VALUE)table[insn])
#else
-#define INSN_CODE(insn) (insn)
+#define INSN_CODE(insn) ((VALUE)(insn))
#endif
st_data_t insn;
encoded_insn_data = st_init_numtable_with_size(VM_INSTRUCTION_SIZE / 2);
diff --git a/iseq.h b/iseq.h
index e7db9b951f..2f83e7336d 100644
--- a/iseq.h
+++ b/iseq.h
@@ -31,6 +31,7 @@ RUBY_EXTERN const int ruby_api_version[];
typedef struct rb_iseq_struct rb_iseq_t;
#define rb_iseq_t rb_iseq_t
#endif
+typedef void (*rb_iseq_callback)(const rb_iseq_t *, void *);
extern const ID rb_iseq_shared_exc_local_tbl[];
@@ -113,12 +114,14 @@ struct iseq_compile_data {
struct iseq_compile_data_storage *storage_current;
} insn;
bool in_rescue;
+ bool in_masgn;
int loopval_popped; /* used by NODE_BREAK */
int last_line;
int label_no;
int node_level;
int isolated_depth;
unsigned int ci_index;
+ unsigned int ic_index;
const rb_compile_option_t *option;
struct rb_id_table *ivar_cache_table;
const struct rb_builtin_function *builtin_function_table;
@@ -186,10 +189,6 @@ void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc,
VALUE exception, VALUE body);
void rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *arena);
-/* iseq.c */
-typedef bool rb_iseq_each_i(VALUE *code, VALUE insn, size_t index, void *data);
-void rb_iseq_each(const rb_iseq_t *iseq, size_t start_index, rb_iseq_each_i iterator, void *data);
-
VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos);
diff --git a/lex.c.blt b/lex.c.blt
index 92a4793b00..85727ed00f 100644
--- a/lex.c.blt
+++ b/lex.c.blt
@@ -34,7 +34,7 @@
struct kwtable {short name, id[2], state;};
const struct kwtable *rb_reserved_word(const char *, unsigned int);
#ifndef RIPPER
-static const struct kwtable *reserved_word(/*const char *, unsigned int*/);
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
#define rb_reserved_word(str, len) reserved_word(str, len)
#line 9 "defs/keywords"
struct kwtable;
diff --git a/lib/English.gemspec b/lib/English.gemspec
index 274fb047b8..a08542bcda 100644
--- a/lib/English.gemspec
+++ b/lib/English.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "english"
- spec.version = "0.7.1"
+ spec.version = "0.7.2"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/abbrev.gemspec b/lib/abbrev.gemspec
index 72837ed2ab..c28b960c8c 100644
--- a/lib/abbrev.gemspec
+++ b/lib/abbrev.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "abbrev"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/lib/benchmark/version.rb b/lib/benchmark/version.rb
index 545575f4ab..645966fd80 100644
--- a/lib/benchmark/version.rb
+++ b/lib/benchmark/version.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Benchmark
- VERSION = "0.2.0"
+ VERSION = "0.2.1"
end
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 7df22ab3a5..f83268e9cd 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -41,7 +41,6 @@ module Bundler
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
- autoload :DepProxy, File.expand_path("bundler/dep_proxy", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
autoload :Digest, File.expand_path("bundler/digest", __dir__)
autoload :Dsl, File.expand_path("bundler/dsl", __dir__)
@@ -53,13 +52,12 @@ module Bundler
autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__)
autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__)
autoload :Graph, File.expand_path("bundler/graph", __dir__)
- autoload :IncompleteSpecification, File.expand_path("bundler/incomplete_specification", __dir__)
autoload :Index, File.expand_path("bundler/index", __dir__)
autoload :Injector, File.expand_path("bundler/injector", __dir__)
autoload :Installer, File.expand_path("bundler/installer", __dir__)
autoload :LazySpecification, File.expand_path("bundler/lazy_specification", __dir__)
autoload :LockfileParser, File.expand_path("bundler/lockfile_parser", __dir__)
- autoload :MatchPlatform, File.expand_path("bundler/match_platform", __dir__)
+ autoload :MatchRemoteMetadata, File.expand_path("bundler/match_remote_metadata", __dir__)
autoload :ProcessLock, File.expand_path("bundler/process_lock", __dir__)
autoload :RemoteSpecification, File.expand_path("bundler/remote_specification", __dir__)
autoload :Resolver, File.expand_path("bundler/resolver", __dir__)
@@ -77,11 +75,12 @@ module Bundler
autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__)
autoload :UI, File.expand_path("bundler/ui", __dir__)
autoload :URICredentialsFilter, File.expand_path("bundler/uri_credentials_filter", __dir__)
- autoload :VersionRanges, File.expand_path("bundler/version_ranges", __dir__)
+ autoload :URINormalizer, File.expand_path("bundler/uri_normalizer", __dir__)
+ autoload :SafeMarshal, File.expand_path("bundler/safe_marshal", __dir__)
class << self
def configure
- @configured ||= configure_gem_home_and_path
+ @configure ||= configure_gem_home_and_path
end
def ui
@@ -211,9 +210,10 @@ module Bundler
end
def frozen_bundle?
- frozen = settings[:deployment]
- frozen ||= settings[:frozen]
- frozen
+ frozen = settings[:frozen]
+ return frozen unless frozen.nil?
+
+ settings[:deployment]
end
def locked_gems
@@ -332,9 +332,9 @@ module Bundler
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
rescue ArgumentError
message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
+It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
+Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
EOF
File.world_writable?(path) ? Bundler.ui.warn(message) : raise
raise PathError, "Please fix the world-writable issue with your #{path} directory"
@@ -456,7 +456,7 @@ EOF
end
def local_platform
- return Gem::Platform::RUBY if settings[:force_ruby_platform] || Gem.platforms == [Gem::Platform::RUBY]
+ return Gem::Platform::RUBY if settings[:force_ruby_platform]
Gem::Platform.local
end
@@ -489,41 +489,9 @@ EOF
configured_bundle_path.use_system_gems?
end
- def requires_sudo?
- return @requires_sudo if defined?(@requires_sudo_ran)
-
- sudo_present = which "sudo" if settings.allow_sudo?
-
- if sudo_present
- # the bundle path and subdirectories need to be writable for RubyGems
- # to be able to unpack and install gems without exploding
- path = bundle_path
- path = path.parent until path.exist?
-
- # bins are written to a different location on OS X
- bin_dir = Pathname.new(Bundler.system_bindir)
- bin_dir = bin_dir.parent until bin_dir.exist?
-
- # if any directory is not writable, we need sudo
- files = [path, bin_dir] | Dir[bundle_path.join("build_info/*").to_s] | Dir[bundle_path.join("*").to_s]
- unwritable_files = files.reject {|f| File.writable?(f) }
- sudo_needed = !unwritable_files.empty?
- if sudo_needed
- Bundler.ui.warn "Following files may not be writable, so sudo is needed:\n #{unwritable_files.map(&:to_s).sort.join("\n ")}"
- end
- end
-
- @requires_sudo_ran = true
- @requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
- end
-
def mkdir_p(path, options = {})
- if requires_sudo? && !options[:no_sudo]
- sudo "mkdir -p '#{path}'" unless File.exist?(path)
- else
- SharedHelpers.filesystem_access(path, :write) do |p|
- FileUtils.mkdir_p(p)
- end
+ SharedHelpers.filesystem_access(path, :write) do |p|
+ FileUtils.mkdir_p(p)
end
end
@@ -531,7 +499,7 @@ EOF
if File.file?(executable) && File.executable?(executable)
executable
elsif paths = ENV["PATH"]
- quote = '"'.freeze
+ quote = '"'
paths.split(File::PATH_SEPARATOR).find do |path|
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
executable_path = File.expand_path(executable, path)
@@ -540,41 +508,14 @@ EOF
end
end
- def sudo(str)
- SUDO_MUTEX.synchronize do
- prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " "
- Your user account isn't allowed to install to the system RubyGems.
- You can cancel this installation and run:
-
- bundle config set --local path 'vendor/bundle'
- bundle install
-
- to install the gems into ./vendor/bundle/, or you can enter your password
- and install the bundled gems to RubyGems using sudo.
-
- Password:
- PROMPT
-
- unless @prompted_for_sudo ||= system(%(sudo -k -p "#{prompt}" true))
- raise SudoNotPermittedError,
- "Bundler requires sudo access to install at the moment. " \
- "Try installing again, granting Bundler sudo access when prompted, or installing into a different path."
- end
-
- `sudo -p "#{prompt}" #{str}`
- end
- end
-
def read_file(file)
SharedHelpers.filesystem_access(file, :read) do
File.open(file, "r:UTF-8", &:read)
end
end
- def load_marshal(data)
- Marshal.load(data)
- rescue TypeError => e
- raise MarshalError, "#{e.class}: #{e.message}"
+ def safe_load_marshal(data)
+ load_marshal(data, :marshal_proc => SafeMarshal.proc)
end
def load_gemspec(file, validate = false)
@@ -583,7 +524,7 @@ EOF
@gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
# Protect against caching side-effected gemspecs by returning a
# new instance each time.
- @gemspec_cache[key].dup if @gemspec_cache[key]
+ @gemspec_cache[key]&.dup
end
def load_gemspec_uncached(file, validate = false)
@@ -610,7 +551,7 @@ EOF
def git_present?
return @git_present if defined?(@git_present)
- @git_present = Bundler.which("git") || Bundler.which("git.exe")
+ @git_present = Bundler.which("git#{RbConfig::CONFIG["EXEEXT"]}")
end
def feature_flag
@@ -632,7 +573,7 @@ EOF
@bin_path = nil
@bundler_major_version = nil
@bundle_path = nil
- @configured = nil
+ @configure = nil
@configured_bundle_path = nil
@definition = nil
@load = nil
@@ -665,6 +606,12 @@ EOF
private
+ def load_marshal(data, marshal_proc: nil)
+ Marshal.load(data, marshal_proc)
+ rescue TypeError => e
+ raise MarshalError, "#{e.class}: #{e.message}"
+ end
+
def eval_yaml_gemspec(path, contents)
Kernel.require "psych"
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec
index 38c533b0c1..da50b46225 100644
--- a/lib/bundler/bundler.gemspec
+++ b/lib/bundler/bundler.gemspec
@@ -22,17 +22,15 @@ Gem::Specification.new do |s|
s.summary = "The best way to manage your application's dependencies"
s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
- if s.respond_to?(:metadata=)
- s.metadata = {
- "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
- "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
- "homepage_uri" => "https://bundler.io/",
- "source_code_uri" => "https://github.com/rubygems/rubygems/",
- }
- end
+ s.metadata = {
+ "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler",
+ }
- s.required_ruby_version = ">= 2.3.0"
- s.required_rubygems_version = ">= 2.5.2"
+ s.required_ruby_version = ">= 2.6.0"
+ s.required_rubygems_version = ">= 3.0.1"
s.files = Dir.glob("lib/bundler{.rb,/**/*}", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 5bf0f4fa3a..a3eb494db2 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -10,6 +10,7 @@ module Bundler
AUTO_INSTALL_CMDS = %w[show binstubs outdated exec open console licenses clean].freeze
PARSEABLE_COMMANDS = %w[check config help exec platform show version].freeze
+ EXTENSIONS = ["c", "rust"].freeze
COMMAND_ALIASES = {
"check" => "c",
@@ -22,6 +23,8 @@ module Bundler
}.freeze
def self.start(*)
+ check_deprecated_ext_option(ARGV) if ARGV.include?("--ext")
+
super
ensure
Bundler::SharedHelpers.print_major_deprecations!
@@ -153,6 +156,7 @@ module Bundler
dependency listed in the gemspec file to the newly created Gemfile.
D
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
+ method_option "gemfile", :type => :string, :banner => "Use the specified name for the gemfile instead of 'Gemfile'"
def init
require_relative "cli/init"
Init.new(options.dup).run
@@ -292,6 +296,8 @@ module Bundler
"Prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"Prefer updating to next major version (default)"
+ method_option "pre", :type => :boolean, :banner =>
+ "Always choose the highest allowed version when updating gems, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"Do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
@@ -372,6 +378,7 @@ module Bundler
method_option "group", :aliases => "-g", :type => :string
method_option "source", :aliases => "-s", :type => :string
method_option "require", :aliases => "-r", :type => :string, :banner => "Adds require path to gem. Provide false, or a path as a string."
+ method_option "path", :type => :string
method_option "git", :type => :string
method_option "github", :type => :string
method_option "branch", :type => :string
@@ -503,6 +510,7 @@ module Bundler
subcommand "config", Config
desc "open GEM", "Opens the source directory of the given bundled gem"
+ method_option "path", :type => :string, :lazy_default => "", :banner => "Open relative path of the gem source."
def open(name)
require_relative "cli/open"
Open.new(options, name).run
@@ -516,7 +524,7 @@ module Bundler
end
end
- desc "version", "Prints the bundler's version information"
+ desc "version", "Prints Bundler version information"
def version
cli_help = current_command.name == "cli_help"
if cli_help || ARGV.include?("version")
@@ -573,7 +581,7 @@ module Bundler
method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR",
:lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? },
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
- method_option :ext, :type => :boolean, :default => false, :desc => "Generate the boilerplate for C extension code"
+ method_option :ext, :type => :string, :desc => "Generate the boilerplate for C extension code.", :enum => EXTENSIONS
method_option :git, :type => :boolean, :default => true, :desc => "Initialize a git repo inside your library."
method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`."
method_option :rubocop, :type => :boolean, :desc => "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
@@ -581,7 +589,7 @@ module Bundler
method_option :test, :type => :string, :lazy_default => Bundler.settings["gem.test"] || "", :aliases => "-t", :banner => "Use the specified test framework for your library",
:desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
- :desc => "Generate CI configuration, either GitHub Actions, Travis CI, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|travis|gitlab|circle)`"
+ :desc => "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
:desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
@@ -619,7 +627,7 @@ module Bundler
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"Only print out changes, do not clean gems"
method_option "force", :type => :boolean, :default => false, :banner =>
- "Forces clean even if --path is not set"
+ "Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application."
def clean
require_relative "cli/clean"
Clean.new(options.dup).run
@@ -667,10 +675,14 @@ module Bundler
"If updating, prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"If updating, prefer updating to next major version (default)"
+ method_option "pre", :type => :boolean, :banner =>
+ "If updating, always choose the highest allowed version, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
"If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
+ method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
+ "Update the locked version of bundler"
def lock
require_relative "cli/lock"
Lock.new(options).run
@@ -748,6 +760,38 @@ module Bundler
end
end
+ def self.check_deprecated_ext_option(arguments)
+ # when deprecated version of `--ext` is called
+ # print out deprecation warning and pretend `--ext=c` was provided
+ if deprecated_ext_value?(arguments)
+ SharedHelpers.major_deprecation 2, "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
+ arguments[arguments.index("--ext")] = "--ext=c"
+ end
+ end
+
+ def self.deprecated_ext_value?(arguments)
+ index = arguments.index("--ext")
+ next_argument = arguments[index+1]
+
+ # it is ok when --ext is followed with valid extension value
+ # for example `bundle gem hello --ext c`
+ return false if EXTENSIONS.include?(next_argument)
+
+ # deprecated call when --ext is called with no value in last position
+ # for example `bundle gem hello_gem --ext`
+ return true if next_argument.nil?
+
+ # deprecated call when --ext is followed by other parameter
+ # for example `bundle gem --ext --no-ci hello_gem`
+ return true if next_argument.start_with?("-")
+
+ # deprecated call when --ext is followed by gem name
+ # for example `bundle gem --ext hello_gem`
+ return true if next_argument
+
+ false
+ end
+
private
# Automatically invoke `bundle install` and resume if
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
index 5bcf30d82d..08fa6547fb 100644
--- a/lib/bundler/cli/add.rb
+++ b/lib/bundler/cli/add.rb
@@ -40,7 +40,7 @@ module Bundler
raise InvalidOption, "Please specify gems to add." if gems.empty?
version.to_a.each do |v|
- raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN =~ v.to_s
+ raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN.match?(v.to_s)
end
end
end
diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb
index 639c01ff39..fc2fad47a5 100644
--- a/lib/bundler/cli/binstubs.rb
+++ b/lib/bundler/cli/binstubs.rb
@@ -11,7 +11,7 @@ module Bundler
def run
Bundler.definition.validate_runtime!
path_option = options["path"]
- path_option = nil if path_option && path_option.empty?
+ path_option = nil if path_option&.empty?
Bundler.settings.set_command_option :bin, path_option if options["path"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
installer = Installer.new(Bundler.root, Bundler.definition)
@@ -40,7 +40,11 @@ module Bundler
end
if options[:standalone]
- next Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") if gem_name == "bundler"
+ if gem_name == "bundler"
+ Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") unless options[:all]
+ next
+ end
+
Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index 65c51337d2..cc1f37f0c3 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -17,7 +17,7 @@ module Bundler
begin
definition.resolve_only_locally!
not_installed = definition.missing_specs
- rescue GemNotFound, VersionConflict
+ rescue GemNotFound, SolveFailure
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index 0d83a1c07e..d654406f65 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -111,6 +111,7 @@ module Bundler
definition.gem_version_promoter.tap do |gvp|
gvp.level = patch_level.first || :major
gvp.strict = options[:strict] || options["filter-strict"]
+ gvp.pre = options[:pre]
end
end
diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb
index 97b8dc0663..1eb8ea8254 100644
--- a/lib/bundler/cli/console.rb
+++ b/lib/bundler/cli/console.rb
@@ -30,9 +30,9 @@ module Bundler
def get_constant(name)
const_name = {
- "pry" => :Pry,
+ "pry" => :Pry,
"ripl" => :Ripl,
- "irb" => :IRB,
+ "irb" => :IRB,
}[name]
Object.const_get(const_name)
rescue NameError
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
index 74444ad0ce..e299a5a8c2 100644
--- a/lib/bundler/cli/doctor.rb
+++ b/lib/bundler/cli/doctor.rb
@@ -73,12 +73,10 @@ module Bundler
definition.specs.each do |spec|
bundles_for_gem(spec).each do |bundle|
bad_paths = dylibs(bundle).select do |f|
- begin
- Fiddle.dlopen(f)
- false
- rescue Fiddle::DLError
- true
- end
+ Fiddle.dlopen(f)
+ false
+ rescue Fiddle::DLError
+ true
end
if bad_paths.any?
broken_links[spec] ||= []
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index c4c76d1b69..7f1200f4a0 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -15,7 +15,7 @@ module Bundler
"test-unit" => "3.0",
}.freeze
- attr_reader :options, :gem_name, :thor, :name, :target
+ attr_reader :options, :gem_name, :thor, :name, :target, :extension
def initialize(options, gem_name, thor)
@options = options
@@ -28,7 +28,11 @@ module Bundler
@name = @gem_name
@target = SharedHelpers.pwd.join(gem_name)
- validate_ext_name if options[:ext]
+ @extension = options[:ext]
+
+ validate_ext_name if @extension
+ validate_rust_builder_rubygems_version if @extension == "rust"
+ travis_removal_info
end
def run
@@ -55,21 +59,22 @@ module Bundler
end
config = {
- :name => name,
+ :name => name,
:underscored_name => underscored_name,
- :namespaced_path => namespaced_path,
- :makefile_path => "#{underscored_name}/#{underscored_name}",
- :constant_name => constant_name,
- :constant_array => constant_array,
- :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
- :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
- :test => options[:test],
- :ext => options[:ext],
- :exe => options[:exe],
- :bundler_version => bundler_dependency_version,
- :git => use_git,
- :github_username => github_username.empty? ? "[USERNAME]" : github_username,
+ :namespaced_path => namespaced_path,
+ :makefile_path => "#{underscored_name}/#{underscored_name}",
+ :constant_name => constant_name,
+ :constant_array => constant_array,
+ :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
+ :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
+ :test => options[:test],
+ :ext => extension,
+ :exe => options[:exe],
+ :bundler_version => bundler_dependency_version,
+ :git => use_git,
+ :github_username => github_username.empty? ? "[USERNAME]" : github_username,
:required_ruby_version => required_ruby_version,
+ :rust_builder_required_rubygems_version => rust_builder_required_rubygems_version,
:minitest_constant_name => minitest_constant_name,
}
ensure_safe_gem_name(name, constant_array)
@@ -132,8 +137,6 @@ module Bundler
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
- when "travis"
- templates.merge!("travis.yml.tt" => ".travis.yml")
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
when "circle"
@@ -188,14 +191,23 @@ module Bundler
templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
- if options[:ext]
+ if extension == "c"
templates.merge!(
- "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb",
+ "ext/newgem/extconf-c.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
"ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
)
end
+ if extension == "rust"
+ templates.merge!(
+ "Cargo.toml.tt" => "Cargo.toml",
+ "ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
+ "ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
+ "ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
+ )
+ end
+
if target.exist? && !target.directory?
Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`."
exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError]
@@ -270,7 +282,7 @@ module Bundler
Bundler.ui.info hint_text("test")
result = Bundler.ui.ask "Enter a test framework. rspec/minitest/test-unit/(none):"
- if result =~ /rspec|minitest|test-unit/
+ if /rspec|minitest|test-unit/.match?(result)
test_framework = result
else
test_framework = false
@@ -306,12 +318,11 @@ module Bundler
"* CircleCI: https://circleci.com/\n" \
"* GitHub Actions: https://github.com/features/actions\n" \
"* GitLab CI: https://docs.gitlab.com/ee/ci/\n" \
- "* Travis CI: https://travis-ci.org/\n" \
"\n"
Bundler.ui.info hint_text("ci")
- result = Bundler.ui.ask "Enter a CI service. github/travis/gitlab/circle/(none):"
- if result =~ /github|travis|gitlab|circle/
+ result = Bundler.ui.ask "Enter a CI service. github/gitlab/circle/(none):"
+ if /github|gitlab|circle/.match?(result)
ci_template = result
else
ci_template = false
@@ -342,7 +353,7 @@ module Bundler
Bundler.ui.info hint_text("linter")
result = Bundler.ui.ask "Enter a linter. rubocop/standard/(none):"
- if result =~ /rubocop|standard/
+ if /rubocop|standard/.match?(result)
linter_template = result
else
linter_template = false
@@ -389,7 +400,7 @@ module Bundler
end
def ensure_safe_gem_name(name, constant_array)
- if name =~ /^\d/
+ if /^\d/.match?(name)
Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
exit 1
end
@@ -415,28 +426,39 @@ module Bundler
thor.run(%(#{editor} "#{file}"))
end
+ def rust_builder_required_rubygems_version
+ "3.3.11"
+ end
+
def required_ruby_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "2.3.0"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "2.4.0"
- elsif Gem.ruby_version < Gem::Version.new("2.6.a") then "2.5.0"
- else
- "2.6.0"
- end
+ "2.6.0"
end
def rubocop_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.81.0"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.12"
- else
- "1.21"
- end
+ "1.21"
end
def standard_version
- if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.2.5"
- elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.0"
- else
- "1.3"
+ "1.3"
+ end
+
+ # TODO: remove at next minor release
+ def travis_removal_info
+ if options[:ci] == "travis"
+ Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator."
+ exit 1
+ end
+
+ if Bundler.settings["gem.ci"] == "travis"
+ Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator, but it is present in bundle config. Please configure another provider using `bundle config set gem.ci SERVICE` (where SERVICE is one of github/gitlab/circle) or unset configuration using `bundle config unset gem.ci`."
+ exit 1
+ end
+ end
+
+ def validate_rust_builder_rubygems_version
+ if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
+ Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
+ exit 1
end
end
end
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
index 0545ce8c75..36c7a58f12 100644
--- a/lib/bundler/cli/info.rb
+++ b/lib/bundler/cli/info.rb
@@ -33,7 +33,7 @@ module Bundler
def default_gem_spec(gem_name)
return unless Gem::Specification.respond_to?(:find_all_by_name)
gem_spec = Gem::Specification.find_all_by_name(gem_name).last
- return gem_spec if gem_spec && gem_spec.respond_to?(:default_gem?) && gem_spec.default_gem?
+ return gem_spec if gem_spec&.default_gem?
end
def spec_not_found(gem_name)
diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb
index e4f8229c48..246b9d6460 100644
--- a/lib/bundler/cli/init.rb
+++ b/lib/bundler/cli/init.rb
@@ -32,7 +32,11 @@ module Bundler
file << spec.to_gemfile
end
else
- FileUtils.cp(File.expand_path("../templates/#{gemfile}", __dir__), gemfile)
+ File.open(File.expand_path("../templates/Gemfile", __dir__), "r") do |template|
+ File.open(gemfile, "wb") do |destination|
+ IO.copy_stream(template, destination)
+ end
+ end
end
puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}"
@@ -41,7 +45,7 @@ module Bundler
private
def gemfile
- @gemfile ||= Bundler.preferred_gemfile_name
+ @gemfile ||= options[:gemfile] || Bundler.preferred_gemfile_name
end
end
end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index 851ae9b840..c71bcf159f 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -94,9 +94,8 @@ module Bundler
def warn_if_root
return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
- Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
- "if it is needed, and installing your bundle as root will break this " \
- "application for all non-root users on this machine.", :wrap => true
+ Bundler.ui.warn "Don't run Bundler as root. Installing your bundle as root " \
+ "will break this application for all non-root users on this machine.", :wrap => true
end
def dependencies_count_for(definition)
@@ -155,7 +154,7 @@ module Bundler
end
bin_option = options["binstubs"]
- bin_option = nil if bin_option && bin_option.empty?
+ bin_option = nil if bin_option&.empty?
Bundler.settings.set_command_option :bin, bin_option if options["binstubs"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index 7d613a6644..cb3ed27138 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -15,19 +15,22 @@ module Bundler
end
print = options[:print]
- ui = Bundler.ui
- Bundler.ui = UI::Silent.new if print
+ previous_ui_level = Bundler.ui.level
+ Bundler.ui.level = "silent" if print
Bundler::Fetcher.disable_endpoint = options["full-index"]
update = options[:update]
conservative = options[:conservative]
+ bundler = options[:bundler]
if update.is_a?(Array) # unlocking specific gems
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
update = { :gems => update, :conservative => conservative }
- elsif update
- update = { :conservative => conservative } if conservative
+ elsif update && conservative
+ update = { :conservative => conservative }
+ elsif update && bundler
+ update = { :bundler => bundler }
end
definition = Bundler.definition(update)
@@ -61,7 +64,7 @@ module Bundler
definition.lock(file)
end
- Bundler.ui = ui
+ Bundler.ui.level = previous_ui_level
end
end
end
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
index ea504344f3..8522ec92d6 100644
--- a/lib/bundler/cli/open.rb
+++ b/lib/bundler/cli/open.rb
@@ -2,23 +2,25 @@
module Bundler
class CLI::Open
- attr_reader :options, :name
+ attr_reader :options, :name, :path
def initialize(options, name)
@options = options
@name = name
+ @path = options[:path] unless options[:path].nil?
end
def run
+ raise InvalidOption, "Cannot specify `--path` option without a value" if !@path.nil? && @path.empty?
editor = [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
return unless spec = Bundler::CLI::Common.select_spec(name, :regex_match)
if spec.default_gem?
Bundler.ui.info "Unable to open #{name} because it's a default gem, so the directory it would normally be installed to does not exist."
else
- path = spec.full_gem_path
- Dir.chdir(path) do
+ root_path = spec.full_gem_path
+ Dir.chdir(root_path) do
require "shellwords"
- command = Shellwords.split(editor) + [path]
+ command = Shellwords.split(editor) << File.join([root_path, path].compact)
Bundler.with_original_env do
system(*command)
end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index e9f93fec39..68c701aefb 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -111,9 +111,7 @@ module Bundler
end.compact
if options[:parseable]
- relevant_outdated_gems.each do |gems|
- print_gems(gems)
- end
+ print_gems(relevant_outdated_gems)
else
print_gems_table(relevant_outdated_gems)
end
@@ -196,7 +194,7 @@ module Bundler
end
current_version = "#{current_spec.version}#{current_spec.git_version}"
- if dependency && dependency.specific?
+ if dependency&.specific?
dependency_version = %(, requested #{dependency.requirement})
end
diff --git a/lib/bundler/cli/platform.rb b/lib/bundler/cli/platform.rb
index 73da8cf80e..32d68abbb1 100644
--- a/lib/bundler/cli/platform.rb
+++ b/lib/bundler/cli/platform.rb
@@ -8,12 +8,12 @@ module Bundler
end
def run
- platforms, ruby_version = Bundler.ui.silence do
- locked_ruby_version = Bundler.locked_gems && Bundler.locked_gems.ruby_version&.gsub(/p\d+\Z/, "")
- gemfile_ruby_version = Bundler.definition.ruby_version && Bundler.definition.ruby_version.single_version_string
- [Bundler.definition.platforms.map {|p| "* #{p}" },
- locked_ruby_version || gemfile_ruby_version]
+ ruby_version = if Bundler.locked_gems
+ Bundler.locked_gems.ruby_version&.gsub(/p\d+\Z/, "")
+ else
+ Bundler.definition.ruby_version&.single_version_string
end
+
output = []
if options[:ruby]
@@ -23,6 +23,8 @@ module Bundler
output << "No ruby version specified"
end
else
+ platforms = Bundler.definition.platforms.map {|p| "* #{p}" }
+
output << "Your platform is: #{Gem::Platform.local}"
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
index 2d83777139..0b43581c11 100644
--- a/lib/bundler/compact_index_client/cache.rb
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -68,7 +68,7 @@ module Bundler
def info_path(name)
name = name.to_s
- if name =~ /[^a-z0-9_-]/
+ if /[^a-z0-9_-]/.match?(name)
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
info_roots.last.join(name)
else
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
index 5b430dfbe2..0f7bf9bb50 100644
--- a/lib/bundler/compact_index_client/updater.rb
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -20,63 +20,64 @@ module Bundler
def initialize(fetcher)
@fetcher = fetcher
- require_relative "../vendored_tmpdir"
end
def update(local_path, remote_path, retrying = nil)
headers = {}
- Bundler::Dir.mktmpdir("bundler-compact-index-") do |local_temp_dir|
- local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)
-
- # first try to fetch any new bytes on the existing file
- if retrying.nil? && local_path.file?
- copy_file local_path, local_temp_path
-
- headers["If-None-Match"] = etag_for(local_temp_path)
- headers["Range"] =
- if local_temp_path.size.nonzero?
- # Subtract a byte to ensure the range won't be empty.
- # Avoids 416 (Range Not Satisfiable) responses.
- "bytes=#{local_temp_path.size - 1}-"
- else
- "bytes=#{local_temp_path.size}-"
- end
- end
+ local_temp_path = local_path.sub(/$/, ".#{$$}")
+ local_temp_path = local_temp_path.sub(/$/, ".retrying") if retrying
+ local_temp_path = local_temp_path.sub(/$/, ".tmp")
- response = @fetcher.call(remote_path, headers)
- return nil if response.is_a?(Net::HTTPNotModified)
+ # first try to fetch any new bytes on the existing file
+ if retrying.nil? && local_path.file?
+ copy_file local_path, local_temp_path
- content = response.body
+ headers["If-None-Match"] = etag_for(local_temp_path)
+ headers["Range"] =
+ if local_temp_path.size.nonzero?
+ # Subtract a byte to ensure the range won't be empty.
+ # Avoids 416 (Range Not Satisfiable) responses.
+ "bytes=#{local_temp_path.size - 1}-"
+ else
+ "bytes=#{local_temp_path.size}-"
+ end
+ end
- etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
- correct_response = SharedHelpers.filesystem_access(local_temp_path) do
- if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
- local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
+ response = @fetcher.call(remote_path, headers)
+ return nil if response.is_a?(Net::HTTPNotModified)
- etag_for(local_temp_path) == etag
- else
- local_temp_path.open("wb") {|f| f << content }
+ content = response.body
- etag.length.zero? || etag_for(local_temp_path) == etag
- end
- end
+ etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
+ correct_response = SharedHelpers.filesystem_access(local_temp_path) do
+ if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
+ local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
- if correct_response
- SharedHelpers.filesystem_access(local_path) do
- FileUtils.mv(local_temp_path, local_path)
- end
- return nil
+ etag_for(local_temp_path) == etag
+ else
+ local_temp_path.open("wb") {|f| f << content }
+
+ etag.length.zero? || etag_for(local_temp_path) == etag
end
+ end
- if retrying
- raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
+ if correct_response
+ SharedHelpers.filesystem_access(local_path) do
+ FileUtils.mv(local_temp_path, local_path)
end
+ return nil
+ end
- update(local_path, remote_path, :retrying)
+ if retrying
+ raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
end
+
+ update(local_path, remote_path, :retrying)
rescue Zlib::GzipFile::Error
raise Bundler::HTTPError
+ ensure
+ FileUtils.remove_file(local_temp_path) if File.exist?(local_temp_path)
end
def etag_for(path)
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index 36f26b7ab4..f009b07ad7 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -22,6 +22,8 @@ module Bundler
2.7
3.0
3.1
+ 3.2
+ 3.3
].freeze
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
@@ -36,17 +38,18 @@ module Bundler
rbx
ruby
truffleruby
+ windows
x64_mingw
].freeze
def ruby?
return true if Bundler::GemHelpers.generic_local_platform == Gem::Platform::RUBY
- !mswin? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
+ !windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
end
def mri?
- !mswin? && RUBY_ENGINE == "ruby"
+ !windows? && RUBY_ENGINE == "ruby"
end
def rbx?
@@ -65,16 +68,24 @@ module Bundler
RUBY_ENGINE == "truffleruby"
end
- def mswin?
+ def windows?
Gem.win_platform?
end
+ def mswin?
+ # For backwards compatibility
+ windows?
+
+ # TODO: This should correctly be:
+ # windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin32" && Bundler.local_platform.cpu == "x86"
+ end
+
def mswin64?
- Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
+ windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end
def mingw?
- Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
+ windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end
def x64_mingw?
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 0ab0451695..564530a98c 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -16,7 +16,6 @@ module Bundler
:locked_deps,
:locked_gems,
:platforms,
- :requires,
:ruby_version,
:lockfile,
:gemfiles
@@ -77,9 +76,13 @@ module Bundler
@lockfile = lockfile
@lockfile_contents = String.new
+
@locked_bundler_version = nil
- @locked_ruby_version = nil
+ @resolved_bundler_version = nil
+
+ @locked_ruby_version = nil
@new_platform = nil
+ @removed_platform = nil
if lockfile && File.exist?(lockfile)
@lockfile_contents = Bundler.read_file(lockfile)
@@ -106,6 +109,7 @@ module Bundler
@locked_gems = nil
@locked_deps = {}
@locked_specs = SpecSet.new([])
+ @originally_locked_specs = @locked_specs
@locked_sources = []
@locked_platforms = []
end
@@ -129,7 +133,7 @@ module Bundler
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
- add_current_platform unless current_ruby_platform_locked? || Bundler.frozen_bundle?
+ add_current_platform unless Bundler.frozen_bundle?
converge_path_sources_to_gemspec_sources
@path_changes = converge_paths
@@ -138,31 +142,18 @@ module Bundler
if @unlock[:conservative]
@unlock[:gems] ||= @dependencies.map(&:name)
else
- eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
- @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name)
+ eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
+ @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
end
@dependency_changes = converge_dependencies
@local_changes = converge_locals
- @reresolve = nil
-
- @requires = compute_requires
+ @missing_lockfile_dep = check_missing_lockfile_dep
end
def gem_version_promoter
- @gem_version_promoter ||= begin
- locked_specs =
- if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty?
- # Definition uses an empty set of locked_specs to indicate all gems
- # are unlocked, but GemVersionPromoter needs the locked_specs
- # for conservative comparison.
- Bundler::SpecSet.new(@locked_gems.specs)
- else
- @locked_specs
- end
- GemVersionPromoter.new(locked_specs, @unlock[:gems])
- end
+ @gem_version_promoter ||= GemVersionPromoter.new
end
def resolve_only_locally!
@@ -171,13 +162,6 @@ module Bundler
resolve
end
- def resolve_prefering_local!
- @prefer_local = true
- @remote = true
- sources.remote!
- resolve
- end
-
def resolve_with_cache!
sources.cached!
resolve
@@ -189,6 +173,23 @@ module Bundler
resolve
end
+ def resolution_mode=(options)
+ if options["local"]
+ @remote = false
+ else
+ @remote = true
+ @prefer_local = options["prefer-local"]
+ end
+ end
+
+ def setup_sources_for_resolve
+ if @remote == false
+ sources.cached!
+ else
+ sources.remote!
+ end
+ end
+
# For given dependency list returns a SpecSet with Gemspec of all the required
# dependencies.
# 1. The method first resolves the dependencies specified in Gemfile
@@ -218,6 +219,8 @@ module Bundler
true
rescue BundlerError => e
@resolve = nil
+ @resolver = nil
+ @resolution_packages = nil
@specs = nil
@gem_version_promoter = nil
@@ -234,8 +237,16 @@ module Bundler
end
def current_dependencies
+ filter_relevant(dependencies)
+ end
+
+ def current_locked_dependencies
+ filter_relevant(locked_dependencies)
+ end
+
+ def filter_relevant(dependencies)
dependencies.select do |d|
- d.should_include? && !d.gem_platforms(@platforms).empty?
+ d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
end
end
@@ -259,10 +270,9 @@ module Bundler
def dependencies_for(groups)
groups.map!(&:to_sym)
- deps = current_dependencies.reject do |d|
+ current_dependencies.reject do |d|
(d.groups & groups).empty?
end
- expand_dependencies(deps)
end
# Resolve all the dependencies specified in Gemfile. It ensures that
@@ -274,21 +284,21 @@ module Bundler
@resolve ||= if Bundler.frozen_bundle?
Bundler.ui.debug "Frozen, using resolution from the lockfile"
@locked_specs
- elsif !unlocking? && nothing_changed?
+ elsif no_resolve_needed?
if deleted_deps.any?
- Bundler.ui.debug("Some dependencies were deleted, using a subset of the resolution from the lockfile")
+ Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
else
- Bundler.ui.debug("Found no changes, using resolution from the lockfile")
- if @locked_gems.may_include_redundant_platform_specific_gems?
+ Bundler.ui.debug "Found no changes, using resolution from the lockfile"
+ if @removed_platform || @locked_gems.may_include_redundant_platform_specific_gems?
SpecSet.new(filter_specs(@locked_specs, @dependencies))
else
@locked_specs
end
end
else
- Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
- @reresolve = reresolve
+ Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
+ start_resolution
end
end
@@ -307,11 +317,11 @@ module Bundler
# Convert to \r\n if the existing lock has them
# i.e., Windows with `git config core.autocrlf=true`
- contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
+ contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
if @locked_bundler_version
locked_major = @locked_bundler_version.segments.first
- current_major = Gem::Version.create(Bundler::VERSION).segments.first
+ current_major = bundler_version_to_lock.segments.first
updating_major = locked_major < current_major
end
@@ -351,27 +361,16 @@ module Bundler
end
end
+ def bundler_version_to_lock
+ @resolved_bundler_version || Bundler.gem_version
+ end
+
def to_lock
require_relative "lockfile_generator"
LockfileGenerator.generate(self)
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
- msg = String.new
- msg << "You are trying to install in deployment mode after changing\n" \
- "your Gemfile. Run `bundle install` elsewhere and add the\n" \
- "updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control."
-
- unless explicit_flag
- suggested_command = if Bundler.settings.locations("frozen").keys.&([:global, :local]).any?
- "bundle config unset frozen"
- elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any?
- "bundle config unset deployment"
- end
- msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
- "freeze \nby running `#{suggested_command}`."
- end
-
added = []
deleted = []
changed = []
@@ -385,32 +384,36 @@ module Bundler
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
both_sources = Hash.new {|h, k| h[k] = [] }
- @dependencies.each {|d| both_sources[d.name][0] = d }
-
- locked_dependencies.each do |d|
- next if !Bundler.feature_flag.bundler_3_mode? && @locked_specs[d.name].empty?
-
- both_sources[d.name][1] = d
- end
+ current_dependencies.each {|d| both_sources[d.name][0] = d }
+ current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
both_sources.each do |name, (dep, lock_dep)|
next if dep.nil? || lock_dep.nil?
- gemfile_source = dep.source || sources.default_source
- lock_source = lock_dep.source || sources.default_source
+ gemfile_source = dep.source || default_source
+ lock_source = lock_dep.source || default_source
next if lock_source.include?(gemfile_source)
- gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
- lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
+ gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
+ lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = change_reason
- msg << "\n\n#{reason.split(", ").map(&:capitalize).join("\n")}" unless reason.strip.empty?
+ msg = String.new
+ msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
- msg << "\n"
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control.\n"
+
+ unless explicit_flag
+ suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
+ "bundle config set frozen false"
+ end
+ msg << "If this is a development machine, remove the #{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)} " \
+ "freeze by running `#{suggested_command}`." if suggested_command
+ end
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
end
@@ -459,7 +462,9 @@ module Bundler
end
def remove_platform(platform)
- return if @platforms.delete(Gem::Platform.new(platform))
+ removed_platform = @platforms.delete(Gem::Platform.new(platform))
+ @removed_platform ||= removed_platform
+ return if removed_platform
raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
end
@@ -473,7 +478,11 @@ module Bundler
private :sources
def nothing_changed?
- !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
+ !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler
+ end
+
+ def no_resolve_needed?
+ !unlocking? && nothing_changed?
end
def unlocking?
@@ -482,15 +491,32 @@ module Bundler
private
- def reresolve
- last_resolve = converge_locked_specs
- remove_ruby_from_platforms_if_necessary!(dependencies)
- expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, true)
- Resolver.resolve(expanded_dependencies, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
+ def resolver
+ @resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
+ end
+
+ def expanded_dependencies
+ dependencies_with_bundler + metadata_dependencies
+ end
+
+ def dependencies_with_bundler
+ return dependencies unless @unlocking_bundler
+ return dependencies if dependencies.map(&:name).include?("bundler")
+
+ [Dependency.new("bundler", @unlocking_bundler)] + dependencies
+ end
+
+ def resolution_packages
+ @resolution_packages ||= begin
+ last_resolve = converge_locked_specs
+ remove_ruby_from_platforms_if_necessary!(current_dependencies)
+ packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, :locked_specs => @originally_locked_specs, :unlock => @unlock[:gems], :prerelease => gem_version_promoter.pre?)
+ additional_base_requirements_for_resolve(packages, last_resolve)
+ end
end
def filter_specs(specs, deps)
- SpecSet.new(specs).for(expand_dependencies(deps, true), false, platforms)
+ SpecSet.new(specs).for(deps, false, platforms)
end
def materialize(dependencies)
@@ -514,25 +540,42 @@ module Bundler
raise GemNotFound, "Could not find #{missing_specs_list.join(" nor ")}"
end
- if @reresolve.nil?
- incomplete_specs = specs.incomplete_specs
+ incomplete_specs = specs.incomplete_specs
+ loop do
+ break if incomplete_specs.empty?
- if incomplete_specs.any?
- Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
- @unlock[:gems].concat(incomplete_specs.map(&:name))
- @resolve = reresolve
- specs = resolve.materialize(dependencies)
+ Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
+ setup_sources_for_resolve
+ resolution_packages.delete(incomplete_specs)
+ @resolve = start_resolution
+ specs = resolve.materialize(dependencies)
+
+ still_incomplete_specs = specs.incomplete_specs
+
+ if still_incomplete_specs == incomplete_specs
+ package = resolution_packages.get_package(incomplete_specs.first.name)
+ resolver.raise_not_found! package
end
+
+ incomplete_specs = still_incomplete_specs
end
- bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
+ bundler = sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last
specs["bundler"] = bundler
specs
end
+ def start_resolution
+ result = resolver.start
+
+ @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
+
+ SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
+ end
+
def precompute_source_requirements_for_indirect_dependencies?
- @remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
+ sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
end
def pin_locally_available_names(source_requirements)
@@ -562,6 +605,8 @@ module Bundler
end
def add_current_platform
+ return if current_ruby_platform_locked?
+
add_platform(local_platform)
end
@@ -583,11 +628,13 @@ module Bundler
[@new_platform, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
+ [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
+ [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
].select(&:first).map(&:last).join(", ")
end
- def pretty_dep(dep, source = false)
- SharedHelpers.pretty_dependency(dep, source)
+ def pretty_dep(dep)
+ SharedHelpers.pretty_dependency(dep)
end
# Check if the specs of the given source changed
@@ -624,8 +671,8 @@ module Bundler
Bundler.settings.local_overrides.map do |k, v|
spec = @dependencies.find {|s| s.name == k }
- source = spec && spec.source
- if source && source.respond_to?(:local_override!)
+ source = spec&.source
+ if source&.respond_to?(:local_override!)
source.unlock! if @unlock[:gems].include?(spec.name)
locals << [source, source.local_override!(v)]
end
@@ -637,6 +684,26 @@ module Bundler
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
end
+ def check_missing_lockfile_dep
+ all_locked_specs = @locked_specs.map(&:name) << "bundler"
+
+ missing = @locked_specs.select do |s|
+ s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
+ end
+
+ if missing.any?
+ @locked_specs.delete(missing)
+
+ return missing.first.name
+ end
+
+ return if @dependency_changes
+
+ current_dependencies.find do |d|
+ @locked_specs[d.name].empty? && d.name != "bundler"
+ end&.name
+ end
+
def converge_paths
sources.path_sources.any? do |source|
specs_changed?(source)
@@ -690,6 +757,8 @@ module Bundler
dep.source = sources.get(dep.source)
end
+ next if unlocking?
+
unless locked_dep = @locked_deps[dep.name]
changes = true
next
@@ -715,7 +784,9 @@ module Bundler
# commonly happen if the Gemfile has changed since the lockfile was last
# generated
def converge_locked_specs
- resolve = converge_specs(@locked_specs)
+ converged = converge_specs(@locked_specs)
+
+ resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
diff = nil
@@ -734,26 +805,27 @@ module Bundler
def converge_specs(specs)
converged = []
-
- deps = @dependencies.select do |dep|
- specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
- end
+ deps = []
@specs_that_changed_sources = []
specs.each do |s|
+ name = s.name
dep = @dependencies.find {|d| s.satisfies?(d) }
+ lockfile_source = s.source
- # Replace the locked dependency's source with the equivalent source from the Gemfile
- s.source = if dep && dep.source
- gemfile_source = dep.source
- lockfile_source = s.source
+ if dep
+ gemfile_source = dep.source || default_source
@specs_that_changed_sources << s if gemfile_source != lockfile_source
+ deps << dep if !dep.source || lockfile_source.include?(dep.source)
+ @unlock[:gems] << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
- gemfile_source
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
+ s.source = gemfile_source
else
- sources.get_with_fallback(s.source)
+ # Replace the locked dependency's source with the default source, if the locked source is no longer in the Gemfile
+ s.source = default_source unless sources.get(lockfile_source)
end
next if @unlock[:sources].include?(s.source.name)
@@ -762,9 +834,9 @@ module Bundler
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
new_specs = begin
s.source.specs
- rescue PathError, GitError
+ rescue PathError
# if we won't need the source (according to the lockfile),
- # don't error if the path/git source isn't available
+ # don't error if the path source isn't available
next if specs.
for(requested_dependencies, false).
none? {|locked_spec| locked_spec.source == s.source }
@@ -773,22 +845,23 @@ module Bundler
end
new_spec = new_specs[s].first
-
- # If the spec is no longer in the path source, unlock it. This
- # commonly happens if the version changed in the gemspec
- next unless new_spec
-
- s.dependencies.replace(new_spec.dependencies)
+ if new_spec
+ s.dependencies.replace(new_spec.dependencies)
+ else
+ # If the spec is no longer in the path source, unlock it. This
+ # commonly happens if the version changed in the gemspec
+ @unlock[:gems] << name
+ end
end
- if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
+ if dep.nil? && requested_dependencies.find {|d| name == d.name }
@unlock[:gems] << s.name
else
converged << s
end
end
- SpecSet.new(filter_specs(converged, deps).reject {|s| @unlock[:gems].include?(s.name) })
+ filter_specs(converged, deps)
end
def metadata_dependencies
@@ -798,23 +871,6 @@ module Bundler
]
end
- def expand_dependencies(dependencies, remote = false)
- deps = []
- dependencies.each do |dep|
- dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
- next unless remote || dep.current_platform?
- target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform])
- deps += expand_dependency_with_platforms(dep, target_platforms)
- end
- deps
- end
-
- def expand_dependency_with_platforms(dep, platforms)
- platforms.map do |p|
- DepProxy.get_proxy(dep, p)
- end
- end
-
def source_requirements
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
@@ -822,7 +878,7 @@ module Bundler
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
all_requirements = source_map.all_requirements
all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
- { :default => sources.default_source }.merge(all_requirements)
+ { :default => default_source }.merge(all_requirements)
else
{ :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
@@ -830,12 +886,24 @@ module Bundler
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
- source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
- source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+
+ default_bundler_source = source_requirements["bundler"] || default_source
+
+ if @unlocking_bundler
+ default_bundler_source.add_dependency_names("bundler")
+ else
+ source_requirements[:default_bundler] = default_bundler_source
+ source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+ end
+
verify_changed_sources!
source_requirements
end
+ def default_source
+ sources.default_source
+ end
+
def verify_changed_sources!
@specs_that_changed_sources.each do |s|
if s.source.specs.search(s.name).empty?
@@ -854,7 +922,8 @@ module Bundler
if preserve_unknown_sections
sections_to_ignore = LockfileParser.sections_to_ignore(@locked_bundler_version)
sections_to_ignore += LockfileParser.unknown_sections_in_lockfile(current)
- sections_to_ignore += LockfileParser::ENVIRONMENT_VERSION_SECTIONS
+ sections_to_ignore << LockfileParser::RUBY
+ sections_to_ignore << LockfileParser::BUNDLED unless @unlocking_bundler
pattern = /#{Regexp.union(sections_to_ignore)}\n(\s{2,}.*\n)+/
whitespace_cleanup = /\n{2,}/
current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
@@ -863,24 +932,13 @@ module Bundler
current == proposed
end
- def compute_requires
- dependencies.reduce({}) do |requires, dep|
- next requires unless dep.should_include?
- requires[dep.name] = Array(dep.autorequire || dep.name).map do |file|
- # Allow `require: true` as an alias for `require: <name>`
- file == true ? dep.name : file
- end
- requires
- end
- end
-
- def additional_base_requirements_for_resolve
- return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
- converge_specs(@originally_locked_specs).map do |locked_spec|
- name = locked_spec.name
- dep = Dependency.new(name, ">= #{locked_spec.version}")
- DepProxy.get_proxy(dep, locked_spec.platform)
+ def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
+ return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
+ converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
+ next if locked_spec.source.is_a?(Source::Path)
+ resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
end
+ resolution_packages
end
def remove_ruby_from_platforms_if_necessary!(dependencies)
@@ -888,7 +946,9 @@ module Bundler
Bundler.local_platform == Gem::Platform::RUBY ||
!platforms.include?(Gem::Platform::RUBY) ||
(@new_platform && platforms.last == Gem::Platform::RUBY) ||
- !@originally_locked_specs.incomplete_ruby_specs?(expand_dependencies(dependencies))
+ @path_changes ||
+ @dependency_changes ||
+ !@originally_locked_specs.incomplete_ruby_specs?(dependencies)
remove_platform(Gem::Platform::RUBY)
add_current_platform
diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb
deleted file mode 100644
index a32dc37b49..0000000000
--- a/lib/bundler/dep_proxy.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler
- class DepProxy
- attr_reader :__platform, :dep
-
- @proxies = {}
-
- def self.get_proxy(dep, platform)
- @proxies[[dep, platform]] ||= new(dep, platform).freeze
- end
-
- def initialize(dep, platform)
- @dep = dep
- @__platform = platform
- end
-
- private_class_method :new
-
- alias_method :eql?, :==
-
- def type
- @dep.type
- end
-
- def name
- @dep.name
- end
-
- def requirement
- @dep.requirement
- end
-
- def to_s
- s = name.dup
- s << " (#{requirement})" unless requirement == Gem::Requirement.default
- s << " #{__platform}" unless __platform == Gem::Platform::RUBY
- s
- end
-
- def dup
- raise NoMethodError.new("DepProxy cannot be duplicated")
- end
-
- def clone
- raise NoMethodError.new("DepProxy cannot be cloned")
- end
-
- private
-
- def method_missing(*args, &blk)
- @dep.send(*args, &blk)
- end
- end
-end
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index 7f94079e09..5f17943629 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -7,92 +7,24 @@ require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
- attr_reader :groups, :platforms, :gemfile, :git, :github, :branch, :ref, :force_ruby_platform
+ attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
- # rubocop:disable Naming/VariableNumber
+ ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze
PLATFORM_MAP = {
- :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :ruby_22 => Gem::Platform::RUBY,
- :ruby_23 => Gem::Platform::RUBY,
- :ruby_24 => Gem::Platform::RUBY,
- :ruby_25 => Gem::Platform::RUBY,
- :ruby_26 => Gem::Platform::RUBY,
- :ruby_27 => Gem::Platform::RUBY,
- :ruby_30 => Gem::Platform::RUBY,
- :ruby_31 => Gem::Platform::RUBY,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mri_22 => Gem::Platform::RUBY,
- :mri_23 => Gem::Platform::RUBY,
- :mri_24 => Gem::Platform::RUBY,
- :mri_25 => Gem::Platform::RUBY,
- :mri_26 => Gem::Platform::RUBY,
- :mri_27 => Gem::Platform::RUBY,
- :mri_30 => Gem::Platform::RUBY,
- :mri_31 => Gem::Platform::RUBY,
- :rbx => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :jruby => Gem::Platform::JAVA,
- :jruby_18 => Gem::Platform::JAVA,
- :jruby_19 => Gem::Platform::JAVA,
- :mswin => Gem::Platform::MSWIN,
- :mswin_18 => Gem::Platform::MSWIN,
- :mswin_19 => Gem::Platform::MSWIN,
- :mswin_20 => Gem::Platform::MSWIN,
- :mswin_21 => Gem::Platform::MSWIN,
- :mswin_22 => Gem::Platform::MSWIN,
- :mswin_23 => Gem::Platform::MSWIN,
- :mswin_24 => Gem::Platform::MSWIN,
- :mswin_25 => Gem::Platform::MSWIN,
- :mswin_26 => Gem::Platform::MSWIN,
- :mswin_27 => Gem::Platform::MSWIN,
- :mswin_30 => Gem::Platform::MSWIN,
- :mswin_31 => Gem::Platform::MSWIN,
- :mswin64 => Gem::Platform::MSWIN64,
- :mswin64_19 => Gem::Platform::MSWIN64,
- :mswin64_20 => Gem::Platform::MSWIN64,
- :mswin64_21 => Gem::Platform::MSWIN64,
- :mswin64_22 => Gem::Platform::MSWIN64,
- :mswin64_23 => Gem::Platform::MSWIN64,
- :mswin64_24 => Gem::Platform::MSWIN64,
- :mswin64_25 => Gem::Platform::MSWIN64,
- :mswin64_26 => Gem::Platform::MSWIN64,
- :mswin64_27 => Gem::Platform::MSWIN64,
- :mswin64_30 => Gem::Platform::MSWIN64,
- :mswin64_31 => Gem::Platform::MSWIN64,
- :mingw => Gem::Platform::MINGW,
- :mingw_18 => Gem::Platform::MINGW,
- :mingw_19 => Gem::Platform::MINGW,
- :mingw_20 => Gem::Platform::MINGW,
- :mingw_21 => Gem::Platform::MINGW,
- :mingw_22 => Gem::Platform::MINGW,
- :mingw_23 => Gem::Platform::MINGW,
- :mingw_24 => Gem::Platform::MINGW,
- :mingw_25 => Gem::Platform::MINGW,
- :mingw_26 => Gem::Platform::MINGW,
- :mingw_27 => Gem::Platform::MINGW,
- :mingw_30 => Gem::Platform::MINGW,
- :mingw_31 => Gem::Platform::MINGW,
- :x64_mingw => Gem::Platform::X64_MINGW,
- :x64_mingw_20 => Gem::Platform::X64_MINGW,
- :x64_mingw_21 => Gem::Platform::X64_MINGW,
- :x64_mingw_22 => Gem::Platform::X64_MINGW,
- :x64_mingw_23 => Gem::Platform::X64_MINGW,
- :x64_mingw_24 => Gem::Platform::X64_MINGW,
- :x64_mingw_25 => Gem::Platform::X64_MINGW,
- :x64_mingw_26 => Gem::Platform::X64_MINGW,
- :x64_mingw_27 => Gem::Platform::X64_MINGW,
- :x64_mingw_30 => Gem::Platform::X64_MINGW,
- :x64_mingw_31 => Gem::Platform::X64_MINGW,
- }.freeze
- # rubocop:enable Naming/VariableNumber
+ :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ :rbx => [Gem::Platform::RUBY],
+ :truffleruby => [Gem::Platform::RUBY],
+ :jruby => [Gem::Platform::JAVA, [18, 19]],
+ :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
+ :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
+ :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
+ :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
+ :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
+ }.each_with_object({}) do |(platform, spec), hash|
+ hash[platform] = spec[0]
+ spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
+ end.freeze
def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime
@@ -101,6 +33,7 @@ module Bundler
@autorequire = nil
@groups = Array(options["group"] || :default).map(&:to_sym)
@source = options["source"]
+ @path = options["path"]
@git = options["git"]
@github = options["github"]
@branch = options["branch"]
@@ -109,7 +42,7 @@ module Bundler
@env = options["env"]
@should_include = options.fetch("should_include", true)
@gemfile = options["gemfile"]
- @force_ruby_platform = options["force_ruby_platform"]
+ @force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
@autorequire = Array(options["require"] || []) if options.key?("require")
end
@@ -117,6 +50,7 @@ module Bundler
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
+ return [Gem::Platform::RUBY] if force_ruby_platform
return valid_platforms if @platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
@@ -151,7 +85,7 @@ module Bundler
def to_lock
out = super
out << "!" if source
- out << "\n"
+ out
end
def specific?
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 385fdd4383..03c80a408c 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -41,7 +41,7 @@ module Bundler
end
def eval_gemfile(gemfile, contents = nil)
- expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile && @gemfile.parent)
+ expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
original_gemfile = @gemfile
@gemfile = expanded_gemfile_path
@gemfiles << expanded_gemfile_path
@@ -67,7 +67,6 @@ module Bundler
gemspecs = Gem::Util.glob_files_in_dir("{,*}.gemspec", expanded_path).map {|g| Bundler.load_gemspec(g) }.compact
gemspecs.reject! {|s| s.name != name } if name
- Index.sort_specs(gemspecs)
specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] }
case specs_by_name_and_version.size
@@ -278,8 +277,8 @@ module Bundler
if repo_name =~ GITHUB_PULL_REQUEST_URL
{
"git" => "https://github.com/#{$1}.git",
- "branch" => "refs/pull/#{$2}/head",
- "ref" => nil,
+ "branch" => nil,
+ "ref" => "refs/pull/#{$2}/head",
"tag" => nil,
}
else
@@ -325,7 +324,7 @@ module Bundler
if name.is_a?(Symbol)
raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead)
end
- if name =~ /\s/
+ if /\s/.match?(name)
raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace)
end
raise GemfileError, %(an empty gem name is not valid) if name.empty?
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index e9aa366b41..863544b1f9 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -3,7 +3,7 @@
module Bundler
# used for Creating Specifications from the Gemcutter Endpoint
class EndpointSpecification < Gem::Specification
- include MatchPlatform
+ include MatchRemoteMetadata
attr_reader :name, :version, :platform, :checksum
attr_accessor :source, :remote, :dependencies
@@ -12,7 +12,7 @@ module Bundler
super()
@name = name
@version = Gem::Version.create version
- @platform = platform
+ @platform = Gem::Platform.new(platform)
@spec_fetcher = spec_fetcher
@dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) }
@@ -22,17 +22,6 @@ module Bundler
parse_metadata(metadata)
end
- def required_ruby_version
- @required_ruby_version ||= _remote_specification.required_ruby_version
- end
-
- # A fallback is included because the original version of the specification
- # API didn't include that field, so some marshalled specs in the index have it
- # set to +nil+.
- def required_rubygems_version
- @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
- end
-
def fetch_platform
@platform
end
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
index 1763035a8a..7b1152930e 100644
--- a/lib/bundler/env.rb
+++ b/lib/bundler/env.rb
@@ -75,7 +75,7 @@ module Bundler
end
def self.git_version
- Bundler::Source::Git::GitProxy.new(nil, nil, nil).full_version
+ Bundler::Source::Git::GitProxy.new(nil, nil).full_version
rescue Bundler::Source::Git::GitNotInstalledError
"not installed"
end
@@ -122,7 +122,7 @@ module Bundler
specs = Bundler.rubygems.find_name(name)
out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
end
- if (exe = caller.last.split(":").first) && exe =~ %r{(exe|bin)/bundler?\z}
+ if (exe = caller.last.split(":").first)&.match? %r{(exe|bin)/bundler?\z}
shebang = File.read(exe).lines.first
shebang.sub!(/^#!\s*/, "")
unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby")
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
index 0f08e049d8..57013f5d50 100644
--- a/lib/bundler/environment_preserver.rb
+++ b/lib/bundler/environment_preserver.rb
@@ -2,11 +2,12 @@
module Bundler
class EnvironmentPreserver
- INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL".freeze
+ INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL"
BUNDLER_KEYS = %w[
BUNDLE_BIN_PATH
BUNDLE_GEMFILE
BUNDLER_VERSION
+ BUNDLER_SETUP
GEM_HOME
GEM_PATH
MANPATH
@@ -15,7 +16,7 @@ module Bundler
RUBYLIB
RUBYOPT
].map(&:freeze).freeze
- BUNDLER_PREFIX = "BUNDLER_ORIG_".freeze
+ BUNDLER_PREFIX = "BUNDLER_ORIG_"
def self.from_env
new(env_to_hash(ENV), BUNDLER_KEYS)
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index f10b6cc68f..5839fc6a73 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -21,16 +21,7 @@ module Bundler
class InstallError < BundlerError; status_code(5); end
# Internal error, should be rescued
- class VersionConflict < BundlerError
- attr_reader :conflicts
-
- def initialize(conflicts, msg = nil)
- super(msg)
- @conflicts = conflicts
- end
-
- status_code(6)
- end
+ class SolveFailure < BundlerError; status_code(6); end
class GemNotFound < BundlerError; status_code(7); end
class InstallHookError < BundlerError; status_code(8); end
@@ -55,7 +46,6 @@ module Bundler
class CyclicDependencyError < BundlerError; status_code(21); end
class GemfileLockNotFound < BundlerError; status_code(22); end
class PluginError < BundlerError; status_code(29); end
- class SudoNotPermittedError < BundlerError; status_code(30); end
class ThreadCreationError < BundlerError; status_code(33); end
class APIResponseMismatchError < BundlerError; status_code(34); end
class APIResponseInvalidDependenciesError < BundlerError; status_code(35); end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index e441b941c2..ab2189f7f0 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -37,9 +37,7 @@ module Bundler
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:print_only_version_number) { bundler_3_mode? }
settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? }
- settings_flag(:suppress_install_using_messages) { bundler_3_mode? }
settings_flag(:update_requires_all_flag) { bundler_4_mode? }
- settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_3_mode? }
settings_option(:default_cli_command) { bundler_3_mode? ? :cli_help : :install }
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index e399a50cfd..2119799f68 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -29,9 +29,7 @@ module Bundler
" is a chance you are experiencing a man-in-the-middle attack, but" \
" most likely your system doesn't have the CA certificates needed" \
" for verification. For information about OpenSSL certificates, see" \
- " https://railsapps.github.io/openssl-certificate-verify-failed.html." \
- " To connect without using SSL, edit your Gemfile" \
- " sources and change 'https' to 'http'."
+ " https://railsapps.github.io/openssl-certificate-verify-failed.html."
end
end
@@ -39,9 +37,7 @@ module Bundler
class SSLError < HTTPError
def initialize(msg = nil)
super msg || "Could not load OpenSSL.\n" \
- "You must recompile Ruby with OpenSSL support or change the sources in your " \
- "Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
- "using RVM are available at rvm.io/packages/openssl."
+ "You must recompile Ruby with OpenSSL support."
end
end
@@ -65,6 +61,16 @@ module Bundler
end
end
+ # This error is raised if HTTP authentication is correct, but lacks
+ # necessary permissions.
+ class AuthenticationForbiddenError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Access token could not be authenticated for #{remote_uri}.\n" \
+ "Make sure it's valid and has the necessary scopes configured."
+ end
+ end
+
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency,
@@ -74,7 +80,7 @@ module Bundler
:HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
:HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
FAIL_ERRORS = begin
- fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError]
+ fail_errors = [AuthenticationRequiredError, BadAuthenticationError, AuthenticationForbiddenError, FallbackError]
fail_errors << Gem::Requirement::BadRequirementError
fail_errors.concat(NET_ERRORS.map {|e| Net.const_get(e) })
end.freeze
@@ -106,11 +112,11 @@ module Bundler
uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
if uri.scheme == "file"
path = Bundler.rubygems.correct_for_windows_path(uri.path)
- Bundler.load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
+ Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
- Bundler.load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
+ Bundler.safe_load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
end
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index b23176588f..8d30dec471 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -12,17 +12,15 @@ module Bundler
method = instance_method(method_name)
undef_method(method_name)
define_method(method_name) do |*args, &blk|
- begin
- method.bind(self).call(*args, &blk)
- rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
- raise HTTPError, e.message
- rescue AuthenticationRequiredError
- # Fail since we got a 401 from the server.
- raise
- rescue HTTPError => e
- Bundler.ui.trace(e)
- nil
- end
+ method.bind(self).call(*args, &blk)
+ rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
+ raise HTTPError, e.message
+ rescue AuthenticationRequiredError, BadAuthenticationError
+ # Fail since we got a 401 from the server.
+ raise
+ rescue HTTPError => e
+ Bundler.ui.trace(e)
+ nil
end
end
@@ -42,7 +40,7 @@ module Bundler
deps = begin
parallel_compact_index_client.dependencies(remaining_gems)
rescue TooManyRequestsError
- @bundle_worker.stop if @bundle_worker
+ @bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
serial_compact_index_client.dependencies(remaining_gems)
end
@@ -51,7 +49,7 @@ module Bundler
complete_gems.concat(deps.map(&:first)).uniq!
remaining_gems = next_gems - complete_gems
end
- @bundle_worker.stop if @bundle_worker
+ @bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
gem_info
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
index c52c32fb5b..18b606abb6 100644
--- a/lib/bundler/fetcher/dependency.rb
+++ b/lib/bundler/fetcher/dependency.rb
@@ -34,14 +34,10 @@ module Bundler
returned_gems = spec_list.map(&:first).uniq
specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
- rescue MarshalError
+ rescue MarshalError, HTTPError, GemspecError
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
nil
- rescue HTTPError, GemspecError
- Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
- Bundler.ui.debug "could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`"
- nil
end
def dependency_specs(gem_names)
@@ -55,7 +51,7 @@ module Bundler
gem_list = []
gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
marshalled_deps = downloader.fetch(dependency_api_uri(names)).body
- gem_list.concat(Bundler.load_marshal(marshalled_deps))
+ gem_list.concat(Bundler.safe_load_marshal(marshalled_deps))
end
gem_list
end
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index 0a668eb17c..3062899e0e 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -41,6 +41,8 @@ module Bundler
when Net::HTTPUnauthorized
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
+ when Net::HTTPForbidden
+ raise AuthenticationForbiddenError, uri.host
when Net::HTTPNotFound
raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
else
@@ -61,9 +63,6 @@ module Bundler
req.basic_auth(user, password)
end
connection.request(uri, req)
- rescue NoMethodError => e
- raise unless ["undefined method", "use_ssl="].all? {|snippet| e.message.include? snippet }
- raise LoadError.new("cannot load such file -- openssl")
rescue OpenSSL::SSL::SSLError
raise CertificateFailureError.new(uri)
rescue *HTTP_ERRORS => e
@@ -80,7 +79,7 @@ module Bundler
private
def validate_uri_scheme!(uri)
- return if uri.scheme =~ /\Ahttps?\z/
+ return if /\Ahttps?\z/.match?(uri.scheme)
raise InvalidOption,
"The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
"Did you mean `http` or `https`?"
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
index 6bb9fcc193..c623647f01 100644
--- a/lib/bundler/fetcher/index.rb
+++ b/lib/bundler/fetcher/index.rb
@@ -15,8 +15,7 @@ module Bundler
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
when /403/
- raise BadAuthenticationError, remote_uri if remote_uri.userinfo
- raise AuthenticationRequiredError, remote_uri
+ raise AuthenticationForbiddenError, remote_uri
else
raise HTTPError, "Could not fetch specs from #{display_uri} due to underlying error <#{e.message}>"
end
diff --git a/lib/bundler/force_platform.rb b/lib/bundler/force_platform.rb
new file mode 100644
index 0000000000..249a24ecd1
--- /dev/null
+++ b/lib/bundler/force_platform.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ module ForcePlatform
+ private
+
+ # The `:force_ruby_platform` value used by dependencies for resolution, and
+ # by locked specifications for materialization is `false` by default, except
+ # for TruffleRuby. TruffleRuby generally needs to force the RUBY platform
+ # variant unless the name is explicitly allowlisted.
+
+ def default_force_ruby_platform
+ return false unless RUBY_ENGINE == "truffleruby"
+
+ !Gem::Platform::REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(name)
+ end
+ end
+end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index d0d2a6679a..39afe8a071 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -36,9 +36,6 @@ module Bundler
end
when Thor::Error
Bundler.ui.error error.message
- when LoadError
- raise error unless error.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
- Bundler.ui.error "\nCould not load OpenSSL. #{error.class}: #{error}\n#{error.backtrace.join("\n ")}"
when Interrupt
Bundler.ui.error "\nQuitting..."
Bundler.ui.trace error
@@ -98,7 +95,7 @@ module Bundler
def serialized_exception_for(e)
<<-EOS.gsub(/^ {8}/, "")
#{e.class}: #{e.message}
- #{e.backtrace && e.backtrace.join("\n ").chomp}
+ #{e.backtrace&.join("\n ")&.chomp}
EOS
end
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index 0bbd2d9b75..dcf759cded 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -21,7 +21,7 @@ module Bundler
def gemspec(&block)
gemspec = instance.gemspec
- block.call(gemspec) if block
+ block&.call(gemspec)
gemspec
end
end
@@ -152,8 +152,7 @@ module Bundler
def gem_push_host
env_rubygems_host = ENV["RUBYGEMS_HOST"]
- env_rubygems_host = nil if
- env_rubygems_host && env_rubygems_host.empty?
+ env_rubygems_host = nil if env_rubygems_host&.empty?
allowed_push_host || env_rubygems_host || "rubygems.org"
end
@@ -218,7 +217,7 @@ module Bundler
SharedHelpers.chdir(base) do
outbuf = IO.popen(cmd, :err => [:child, :out], &:read)
status = $?
- block.call(outbuf) if status.success? && block
+ block&.call(outbuf) if status.success?
[outbuf, status]
end
end
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index 0d50d8687b..2e6d788f9c 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -5,7 +5,6 @@ module Bundler
GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
GENERICS = [
[Gem::Platform.new("java"), Gem::Platform.new("java")],
- [Gem::Platform.new("universal-java"), Gem::Platform.new("java")],
[Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")],
[Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")],
[Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")],
diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb
index 3cce3f2139..d281f46eeb 100644
--- a/lib/bundler/gem_version_promoter.rb
+++ b/lib/bundler/gem_version_promoter.rb
@@ -7,14 +7,13 @@ module Bundler
# available dependency versions as found in its index, before returning it to
# to the resolution engine to select the best version.
class GemVersionPromoter
- DEBUG = ENV["BUNDLER_DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER"]
-
- attr_reader :level, :locked_specs, :unlock_gems
+ attr_reader :level
+ attr_accessor :pre
# By default, strict is false, meaning every available version of a gem
# is returned from sort_versions. The order gives preference to the
# requested level (:patch, :minor, :major) but in complicated requirement
- # cases some gems will by necessity by promoted past the requested level,
+ # cases some gems will by necessity be promoted past the requested level,
# or even reverted to older versions.
#
# If strict is set to true, the results from sort_versions will be
@@ -24,24 +23,13 @@ module Bundler
# existing in the referenced source.
attr_accessor :strict
- attr_accessor :prerelease_specified
-
- # Given a list of locked_specs and a list of gems to unlock creates a
- # GemVersionPromoter instance.
+ # Creates a GemVersionPromoter instance.
#
- # @param locked_specs [SpecSet] All current locked specs. Unlike Definition
- # where this list is empty if all gems are being updated, this should
- # always be populated for all gems so this class can properly function.
- # @param unlock_gems [String] List of gem names being unlocked. If empty,
- # all gems will be considered unlocked.
# @return [GemVersionPromoter]
- def initialize(locked_specs = SpecSet.new([]), unlock_gems = [])
+ def initialize
@level = :major
@strict = false
- @locked_specs = locked_specs
- @unlock_gems = unlock_gems
- @sort_versions = {}
- @prerelease_specified = {}
+ @pre = false
end
# @param value [Symbol] One of three Symbols: :major, :minor or :patch.
@@ -55,37 +43,19 @@ module Bundler
@level = v
end
- # Given a Dependency and an Array of SpecGroups of available versions for a
- # gem, this method will return the Array of SpecGroups sorted (and possibly
- # truncated if strict is true) in an order to give preference to the current
- # level (:major, :minor or :patch) when resolution is deciding what versions
- # best resolve all dependencies in the bundle.
- # @param dep [Dependency] The Dependency of the gem.
- # @param spec_groups [SpecGroup] An array of SpecGroups for the same gem
- # named in the @dep param.
- # @return [SpecGroup] A new instance of the SpecGroup Array sorted and
+ # Given a Resolver::Package and an Array of Specifications of available
+ # versions for a gem, this method will return the Array of Specifications
+ # sorted (and possibly truncated if strict is true) in an order to give
+ # preference to the current level (:major, :minor or :patch) when resolution
+ # is deciding what versions best resolve all dependencies in the bundle.
+ # @param package [Resolver::Package] The package being resolved.
+ # @param specs [Specification] An array of Specifications for the package.
+ # @return [Specification] A new instance of the Specification Array sorted and
# possibly filtered.
- def sort_versions(dep, spec_groups)
- before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if DEBUG
-
- @sort_versions[dep] ||= begin
- gem_name = dep.name
+ def sort_versions(package, specs)
+ specs = filter_dep_specs(specs, package) if strict
- # An Array per version returned, different entries for different platforms.
- # We only need the version here so it's ok to hard code this to the first instance.
- locked_spec = locked_specs[gem_name].first
-
- if strict
- filter_dep_specs(spec_groups, locked_spec)
- else
- sort_dep_specs(spec_groups, locked_spec)
- end.tap do |specs|
- if DEBUG
- puts before_result
- puts " after sort_versions: #{debug_format_result(dep, specs).inspect}"
- end
- end
- end
+ sort_dep_specs(specs, package)
end
# @return [bool] Convenience method for testing value of level variable.
@@ -98,80 +68,72 @@ module Bundler
level == :minor
end
+ # @return [bool] Convenience method for testing value of pre variable.
+ def pre?
+ pre == true
+ end
+
private
- def filter_dep_specs(spec_groups, locked_spec)
- res = spec_groups.select do |spec_group|
- if locked_spec && !major?
- gsv = spec_group.version
- lsv = locked_spec.version
+ def filter_dep_specs(specs, package)
+ locked_version = package.locked_version
+ return specs if locked_version.nil? || major?
- must_match = minor? ? [0] : [0, 1]
+ specs.select do |spec|
+ gsv = spec.version
- matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] }
- matches.uniq == [true] ? (gsv >= lsv) : false
- else
- true
- end
- end
+ must_match = minor? ? [0] : [0, 1]
- sort_dep_specs(res, locked_spec)
+ all_match = must_match.all? {|idx| gsv.segments[idx] == locked_version.segments[idx] }
+ all_match && gsv >= locked_version
+ end
end
- def sort_dep_specs(spec_groups, locked_spec)
- return spec_groups unless locked_spec
- @gem_name = locked_spec.name
- @locked_version = locked_spec.version
-
- result = spec_groups.sort do |a, b|
- @a_ver = a.version
- @b_ver = b.version
+ def sort_dep_specs(specs, package)
+ locked_version = package.locked_version
- unless @prerelease_specified[@gem_name]
- a_pre = @a_ver.prerelease?
- b_pre = @b_ver.prerelease?
+ result = specs.sort do |a, b|
+ unless package.prerelease_specified? || pre?
+ a_pre = a.prerelease?
+ b_pre = b.prerelease?
next -1 if a_pre && !b_pre
next 1 if b_pre && !a_pre
end
if major?
- @a_ver <=> @b_ver
- elsif either_version_older_than_locked
- @a_ver <=> @b_ver
- elsif segments_do_not_match(:major)
- @b_ver <=> @a_ver
- elsif !minor? && segments_do_not_match(:minor)
- @b_ver <=> @a_ver
+ a <=> b
+ elsif either_version_older_than_locked?(a, b, locked_version)
+ a <=> b
+ elsif segments_do_not_match?(a, b, :major)
+ b <=> a
+ elsif !minor? && segments_do_not_match?(a, b, :minor)
+ b <=> a
else
- @a_ver <=> @b_ver
+ a <=> b
end
end
- post_sort(result)
+ post_sort(result, package.unlock?, locked_version)
end
- def either_version_older_than_locked
- @a_ver < @locked_version || @b_ver < @locked_version
+ def either_version_older_than_locked?(a, b, locked_version)
+ locked_version && (a.version < locked_version || b.version < locked_version)
end
- def segments_do_not_match(level)
+ def segments_do_not_match?(a, b, level)
index = [:major, :minor].index(level)
- @a_ver.segments[index] != @b_ver.segments[index]
- end
-
- def unlocking_gem?
- unlock_gems.empty? || unlock_gems.include?(@gem_name)
+ a.segments[index] != b.segments[index]
end
# Specific version moves can't always reliably be done during sorting
# as not all elements are compared against each other.
- def post_sort(result)
+ def post_sort(result, unlock, locked_version)
# default :major behavior in Bundler does not do this
return result if major?
- if unlocking_gem?
+ if unlock || locked_version.nil?
result
else
- move_version_to_end(result, @locked_version)
+ move_version_to_end(result, locked_version)
end
end
@@ -179,12 +141,5 @@ module Bundler
move, keep = result.partition {|s| s.version.to_s == version.to_s }
keep.concat(move)
end
-
- def debug_format_result(dep, spec_groups)
- a = [dep.to_s,
- spec_groups.map {|sg| [sg.version, sg.dependencies_for_activated_platforms.map {|dp| [dp.name, dp.requirement.to_s] }] }]
- last_map = a.last.map {|sg_data| [sg_data.first.version, sg_data.last.map {|aa| aa.join(" ") }] }
- [a.first, last_map, level, strict ? :strict : :not_strict]
- end
end
end
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
index 8f52e2f0f0..3c008e63e3 100644
--- a/lib/bundler/graph.rb
+++ b/lib/bundler/graph.rb
@@ -114,10 +114,10 @@ module Bundler
@groups.each do |group|
g.add_nodes(
group, {
- :style => "filled",
+ :style => "filled",
:fillcolor => "#B9B9D5",
- :shape => "box3d",
- :fontsize => 16,
+ :shape => "box3d",
+ :fontsize => 16,
}.merge(@node_options[group])
)
end
diff --git a/lib/bundler/incomplete_specification.rb b/lib/bundler/incomplete_specification.rb
deleted file mode 100644
index 6d0b9b901c..0000000000
--- a/lib/bundler/incomplete_specification.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler
- class IncompleteSpecification
- attr_reader :name, :platform
-
- def initialize(name, platform)
- @name = name
- @platform = platform
- end
- end
-end
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index 00c7a9e00d..b8c599f63a 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -13,8 +13,8 @@ module Bundler
attr_reader :specs, :all_specs, :sources
protected :specs, :all_specs
- RUBY = "ruby".freeze
- NULL = "\0".freeze
+ RUBY = "ruby"
+ NULL = "\0"
def initialize
@sources = []
@@ -57,44 +57,20 @@ module Bundler
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
def search(query)
- sort_specs(unsorted_search(query))
- end
-
- def unsorted_search(query)
results = local_search(query)
-
- seen = results.map(&:full_name).uniq unless @sources.empty?
+ return results unless @sources.any?
@sources.each do |source|
- source.unsorted_search(query).each do |spec|
- next if seen.include?(spec.full_name)
-
- seen << spec.full_name
- results << spec
- end
- end
-
- results
- end
- protected :unsorted_search
-
- def self.sort_specs(specs)
- specs.sort_by do |s|
- platform_string = s.platform.to_s
- [s.version, platform_string == RUBY ? NULL : platform_string]
+ results.concat(source.search(query))
end
- end
-
- def sort_specs(specs)
- self.class.sort_specs(specs)
+ results.uniq(&:full_name)
end
def local_search(query)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
when String then specs_by_name(query)
- when Gem::Dependency then search_by_dependency(query)
- when DepProxy then search_by_dependency(query.dep)
+ when Array then specs_by_name_and_version(*query)
else
raise "You can't search for a #{query.inspect}."
end
@@ -181,20 +157,12 @@ module Bundler
private
- def specs_by_name(name)
- @specs[name].values
+ def specs_by_name_and_version(name, version)
+ specs_by_name(name).select {|spec| spec.version == version }
end
- def search_by_dependency(dependency)
- @cache[dependency] ||= begin
- specs = specs_by_name(dependency.name)
- found = specs.select do |spec|
- next true if spec.source.is_a?(Source::Gemspec)
- dependency.matches_spec?(spec)
- end
-
- found
- end
+ def specs_by_name(name)
+ @specs[name].values
end
EMPTY_SEARCH = [].freeze
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index f6550abe88..cb644a7f69 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -2,7 +2,7 @@
module Bundler
class Injector
- INJECTED_GEMS = "injected gems".freeze
+ INJECTED_GEMS = "injected gems"
def self.inject(new_deps, options = {})
injector = new(new_deps, options)
@@ -70,7 +70,7 @@ module Bundler
show_warning("No gems were removed from the gemfile.") if deps.empty?
- deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." }
+ deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep)} was removed." }
end
# Invalidate the cached Bundler.definition.
@@ -115,13 +115,14 @@ module Bundler
end
source = ", :source => \"#{d.source}\"" unless d.source.nil?
+ path = ", :path => \"#{d.path}\"" unless d.path.nil?
git = ", :git => \"#{d.git}\"" unless d.git.nil?
github = ", :github => \"#{d.github}\"" unless d.github.nil?
branch = ", :branch => \"#{d.branch}\"" unless d.branch.nil?
ref = ", :ref => \"#{d.ref}\"" unless d.ref.nil?
require_path = ", :require => #{convert_autorequire(d.autorequire)}" unless d.autorequire.nil?
- %(gem #{name}#{requirement}#{group}#{source}#{git}#{github}#{branch}#{ref}#{require_path})
+ %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{require_path})
end.join("\n")
end
@@ -234,7 +235,7 @@ module Bundler
gemfile.each_with_index do |line, index|
next unless !line.nil? && line.strip.start_with?(block_name)
- if gemfile[index + 1] =~ /^\s*end\s*$/
+ if /^\s*end\s*$/.match?(gemfile[index + 1])
gemfile[index] = nil
gemfile[index + 1] = nil
end
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index 25e055fbe4..5c184f67a1 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -31,15 +31,16 @@
#
def gemfile(install = false, options = {}, &gemfile)
require_relative "../bundler"
+ Bundler.reset!
opts = options.dup
ui = opts.delete(:ui) { Bundler::UI::Shell.new }
- ui.level = "silent" if opts.delete(:quiet)
+ ui.level = "silent" if opts.delete(:quiet) || !install
+ Bundler.ui = ui
raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
- begin
+ Bundler.with_unbundled_env do
Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir))
- old_gemfile = ENV["BUNDLE_GEMFILE"]
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
@@ -52,7 +53,6 @@ def gemfile(install = false, options = {}, &gemfile)
def definition.lock(*); end
definition.validate_runtime!
- Bundler.ui = install ? ui : Bundler::UI::Silent.new
if install || definition.missing_specs?
Bundler.settings.temporary(:inline => true, :no_install => false) do
installer = Bundler::Installer.install(Bundler.root, definition, :system => true)
@@ -65,11 +65,9 @@ def gemfile(install = false, options = {}, &gemfile)
runtime = Bundler::Runtime.new(nil, definition)
runtime.setup.require
end
- ensure
- if old_gemfile
- ENV["BUNDLE_GEMFILE"] = old_gemfile
- else
- ENV["BUNDLE_GEMFILE"] = ""
- end
+ end
+
+ if ENV["BUNDLE_GEMFILE"].nil?
+ ENV["BUNDLE_GEMFILE"] = ""
end
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index b7b0e36dfd..59b6a6ad22 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -90,7 +90,7 @@ module Bundler
Gem::Specification.reset # invalidate gem specification cache so that installed gems are immediately available
- lock unless Bundler.frozen_bundle?
+ lock
Standalone.new(options[:standalone], @definition).generate if options[:standalone]
end
end
@@ -136,11 +136,7 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = if RUBY_VERSION >= "2.6"
- ERB.new(template, :trim_mode => "-").result(binding)
- else
- ERB.new(template, nil, "-").result(binding)
- end
+ content = ERB.new(template, :trim_mode => "-").result(binding)
File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
if Gem.win_platform? || options[:all_platforms]
@@ -183,11 +179,7 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = if RUBY_VERSION >= "2.6"
- ERB.new(template, :trim_mode => "-").result(binding)
- else
- ERB.new(template, nil, "-").result(binding)
- end
+ content = ERB.new(template, :trim_mode => "-").result(binding)
File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
if Gem.win_platform? || options[:all_platforms]
@@ -226,31 +218,24 @@ module Bundler
requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
path_plugin_files = requested_path_gems.map do |spec|
- begin
- Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
- rescue TypeError
- error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
- raise Gem::InvalidSpecificationException, error_message
- end
+ Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
+ rescue TypeError
+ error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
+ raise Gem::InvalidSpecificationException, error_message
end.flatten
Bundler.rubygems.load_plugin_files(path_plugin_files)
Bundler.rubygems.load_env_plugins
end
def ensure_specs_are_compatible!
- system_ruby = Bundler::RubyVersion.system
- rubygems_version = Bundler.rubygems.version
@definition.specs.each do |spec|
- if required_ruby_version = spec.required_ruby_version
- unless required_ruby_version.satisfied_by?(system_ruby.gem_version)
- raise InstallError, "#{spec.full_name} requires ruby version #{required_ruby_version}, " \
- "which is incompatible with the current version, #{system_ruby}"
- end
+ unless spec.matches_current_ruby?
+ raise InstallError, "#{spec.full_name} requires ruby version #{spec.required_ruby_version}, " \
+ "which is incompatible with the current version, #{Gem.ruby_version}"
end
- next unless required_rubygems_version = spec.required_rubygems_version
- unless required_rubygems_version.satisfied_by?(rubygems_version)
- raise InstallError, "#{spec.full_name} requires rubygems version #{required_rubygems_version}, " \
- "which is incompatible with the current version, #{rubygems_version}"
+ unless spec.matches_current_rubygems?
+ raise InstallError, "#{spec.full_name} requires rubygems version #{spec.required_rubygems_version}, " \
+ "which is incompatible with the current version, #{Gem.rubygems_version}"
end
end
end
@@ -264,17 +249,13 @@ module Bundler
# returns whether or not a re-resolve was needed
def resolve_if_needed(options)
+ @definition.resolution_mode = options
+
if !@definition.unlocking? && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file?
return false if @definition.nothing_changed? && !@definition.missing_specs?
end
- if options["local"]
- @definition.resolve_with_cache!
- elsif options["prefer-local"]
- @definition.resolve_prefering_local!
- else
- @definition.resolve_remotely!
- end
+ @definition.setup_sources_for_resolve
true
end
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index 5b6680e5e1..83a381f592 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -53,10 +53,6 @@ module Bundler
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end
- def missing_lockfile_dependencies(all_spec_names)
- dependencies.reject {|dep| all_spec_names.include? dep.name }
- end
-
# Represents all dependencies
def all_dependencies
@spec.dependencies
@@ -84,8 +80,6 @@ module Bundler
end
def call
- check_for_corrupt_lockfile
-
if @rake
do_install(@rake, 0)
Gem::Specification.reset
@@ -102,7 +96,7 @@ module Bundler
handle_error if failed_specs.any?
@specs
ensure
- worker_pool && worker_pool.stop
+ worker_pool&.stop
end
def check_for_unmet_dependencies
@@ -116,43 +110,19 @@ module Bundler
warning = []
warning << "Your lockfile doesn't include a valid resolution."
- warning << "You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies."
+ warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies."
warning << "The unmet dependencies are:"
unmet_dependencies.each do |spec, unmet_spec_dependencies|
unmet_spec_dependencies.each do |unmet_spec_dependency|
- warning << "* #{unmet_spec_dependency}, depended upon #{spec.full_name}, unsatisfied by #{@specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }.full_name}"
+ found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }
+ warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
end
end
Bundler.ui.warn(warning.join("\n"))
end
- def check_for_corrupt_lockfile
- missing_dependencies = @specs.map do |s|
- [
- s,
- s.missing_lockfile_dependencies(@specs.map(&:name)),
- ]
- end.reject {|a| a.last.empty? }
- return if missing_dependencies.empty?
-
- warning = []
- warning << "Your lockfile was created by an old Bundler that left some things out."
- if @size != 1
- warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time."
- @size = 1
- end
- warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile."
- warning << "The missing gems are:"
-
- missing_dependencies.each do |spec, missing|
- warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}"
- end
-
- Bundler.ui.warn(warning.join("\n"))
- end
-
private
def failed_specs
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
index 2756626f8a..2a8c9a432d 100644
--- a/lib/bundler/installer/standalone.rb
+++ b/lib/bundler/installer/standalone.rb
@@ -52,7 +52,7 @@ module Bundler
def gem_path(path, spec)
full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
- if spec.source.instance_of?(Source::Path)
+ if spec.source.instance_of?(Source::Path) && spec.source.path.absolute?
full_path
else
Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
@@ -84,13 +84,17 @@ module Bundler
def reverse_rubygems_kernel_mixin
<<~END
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
- if k.private_method_defined?(:gem_original_require)
- private_require = k.private_method_defined?(:require)
- k.send(:remove_method, :require)
- k.send(:define_method, :require, k.instance_method(:gem_original_require))
- k.send(:private, :require) if private_require
+ if Gem.respond_to?(:discover_gems_on_require=)
+ Gem.discover_gems_on_require = false
+ else
+ kernel = (class << ::Kernel; self; end)
+ [kernel, ::Kernel].each do |k|
+ if k.private_method_defined?(:gem_original_require)
+ private_require = k.private_method_defined?(:require)
+ k.send(:remove_method, :require)
+ k.send(:define_method, :require, k.instance_method(:gem_original_require))
+ k.send(:private, :require) if private_require
+ end
end
end
END
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 5b40bec5a8..c9b161dc0e 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
-require_relative "match_platform"
+require_relative "force_platform"
module Bundler
class LazySpecification
include MatchPlatform
+ include ForcePlatform
attr_reader :name, :version, :dependencies, :platform
attr_accessor :source, :remote, :force_ruby_platform
@@ -15,11 +16,11 @@ module Bundler
@dependencies = []
@platform = platform || Gem::Platform::RUBY
@source = source
- @specification = nil
+ @force_ruby_platform = default_force_ruby_platform
end
def full_name
- if platform == Gem::Platform::RUBY
+ @full_name ||= if platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
@@ -27,15 +28,15 @@ module Bundler
end
def ==(other)
- identifier == other.identifier
+ full_name == other.full_name
end
def eql?(other)
- identifier.eql?(other.identifier)
+ full_name.eql?(other.full_name)
end
def hash
- identifier.hash
+ full_name.hash
end
##
@@ -78,82 +79,71 @@ module Bundler
def materialize_for_installation
source.local!
- candidates = if source.is_a?(Source::Path) || !ruby_platform_materializes_to_ruby_platform?
- target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : Bundler.local_platform
+ matching_specs = source.specs.search(use_exact_resolved_specifications? ? self : [name, version])
+ return self if matching_specs.empty?
- source.specs.search(Dependency.new(name, version)).select do |spec|
- MatchPlatform.platforms_match?(spec.platform, target_platform)
- end
+ candidates = if use_exact_resolved_specifications?
+ matching_specs
else
- source.specs.search(self)
- end
+ target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform
- return self if candidates.empty?
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
- __materialize__(candidates)
- end
+ specification = __materialize__(installable_candidates, :fallback_to_non_installable => false)
+ return specification unless specification.nil?
- def __materialize__(candidates)
- @specification = begin
- search = candidates.reverse.find do |spec|
- spec.is_a?(StubSpecification) ||
- (spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
- spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version))
- end
- if search.nil? && Bundler.frozen_bundle?
- search = candidates.last
- else
- search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
+ if target_platform != platform
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end
- search
+
+ installable_candidates
end
+
+ __materialize__(candidates)
end
- def respond_to?(*args)
- super || @specification ? @specification.respond_to?(*args) : nil
+ # If in frozen mode, we fallback to a non-installable candidate because by
+ # doing this we avoid re-resolving and potentially end up changing the
+ # lock file, which is not allowed. In that case, we will give a proper error
+ # about the mismatch higher up the stack, right before trying to install the
+ # bad gem.
+ def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
+ search = candidates.reverse.find do |spec|
+ spec.is_a?(StubSpecification) ||
+ (spec.matches_current_ruby? &&
+ spec.matches_current_rubygems?)
+ end
+ if search.nil? && fallback_to_non_installable
+ search = candidates.last
+ else
+ search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
+ end
+ search
end
def to_s
- @__to_s ||= if platform == Gem::Platform::RUBY
+ @to_s ||= if platform == Gem::Platform::RUBY
"#{name} (#{version})"
else
"#{name} (#{version}-#{platform})"
end
end
- def identifier
- @__identifier ||= [name, version, platform_string]
- end
-
def git_version
return unless source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
end
- protected
-
- def platform_string
- platform_string = platform.to_s
- platform_string == Index::RUBY ? Index::NULL : platform_string
- end
-
private
- def to_ary
- nil
- end
-
- def method_missing(method, *args, &blk)
- raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
-
- return super unless respond_to?(method)
-
- @specification.send(method, *args, &blk)
+ def use_exact_resolved_specifications?
+ @use_exact_resolved_specifications ||= !source.is_a?(Source::Path) && ruby_platform_materializes_to_ruby_platform?
end
#
# For backwards compatibility with existing lockfiles, if the most specific
- # locked platform is RUBY, we keep the previous behaviour of resolving the
+ # locked platform is not a specific platform like x86_64-linux or
+ # universal-java-11, then we keep the previous behaviour of resolving the
# best platform variant at materiliazation time. For previous bundler
# versions (before 2.2.0) this was always the case (except when the lockfile
# only included non-ruby platforms), but we're also keeping this behaviour
@@ -161,7 +151,9 @@ module Bundler
# explicitly add a more specific platform.
#
def ruby_platform_materializes_to_ruby_platform?
- !Bundler.most_specific_locked_platform?(generic_local_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
+ generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY
+
+ !Bundler.most_specific_locked_platform?(generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end
end
end
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
index 0578a93fdc..f7ba51b3e6 100644
--- a/lib/bundler/lockfile_generator.rb
+++ b/lib/bundler/lockfile_generator.rb
@@ -45,7 +45,7 @@ module Bundler
# gems with the same name, but different platform
# are ordered consistently
specs.sort_by(&:full_name).each do |spec|
- next if spec.name == "bundler".freeze
+ next if spec.name == "bundler"
out << spec.to_lock
end
end
@@ -60,7 +60,7 @@ module Bundler
handled = []
definition.dependencies.sort_by(&:to_s).each do |dep|
next if handled.include?(dep.name)
- out << dep.to_lock
+ out << dep.to_lock << "\n"
handled << dep.name
end
end
@@ -71,7 +71,7 @@ module Bundler
end
def add_bundled_with
- add_section("BUNDLED WITH", Bundler::VERSION)
+ add_section("BUNDLED WITH", definition.bundler_version_to_lock.to_s)
end
def add_section(name, value)
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 64fff4713d..7360a36752 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -4,15 +4,15 @@ module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
- BUNDLED = "BUNDLED WITH".freeze
- DEPENDENCIES = "DEPENDENCIES".freeze
- PLATFORMS = "PLATFORMS".freeze
- RUBY = "RUBY VERSION".freeze
- GIT = "GIT".freeze
- GEM = "GEM".freeze
- PATH = "PATH".freeze
- PLUGIN = "PLUGIN SOURCE".freeze
- SPECS = " specs:".freeze
+ BUNDLED = "BUNDLED WITH"
+ DEPENDENCIES = "DEPENDENCIES"
+ PLATFORMS = "PLATFORMS"
+ RUBY = "RUBY VERSION"
+ GIT = "GIT"
+ GEM = "GEM"
+ PATH = "PATH"
+ PLUGIN = "PLUGIN SOURCE"
+ SPECS = " specs:"
OPTIONS = /^ ([a-z]+): (.*)$/i.freeze
SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
@@ -26,6 +26,7 @@ module Bundler
KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze
+ deprecate_constant(:ENVIRONMENT_VERSION_SECTIONS)
def self.sections_in_lockfile(lockfile_contents)
lockfile_contents.scan(/^\w[\w ]*$/).uniq
@@ -63,7 +64,7 @@ module Bundler
@state = nil
@specs = {}
- if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
+ if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \
"Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
end
@@ -80,13 +81,13 @@ module Bundler
@state = :ruby
elsif line == BUNDLED
@state = :bundled_with
- elsif line =~ /^[^\s]/
+ elsif /^[^\s]/.match?(line)
@state = nil
elsif @state
send("parse_#{@state}", line)
end
end
- @specs = @specs.values.sort_by(&:identifier)
+ @specs = @specs.values.sort_by(&:full_name)
rescue ArgumentError => e
Bundler.ui.debug(e)
raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \
@@ -100,9 +101,9 @@ module Bundler
private
TYPES = {
- GIT => Bundler::Source::Git,
- GEM => Bundler::Source::Rubygems,
- PATH => Bundler::Source::Path,
+ GIT => Bundler::Source::Git,
+ GEM => Bundler::Source::Rubygems,
+ PATH => Bundler::Source::Path,
PLUGIN => Bundler::Plugin,
}.freeze
@@ -199,7 +200,7 @@ module Bundler
@current_spec.source = @current_source
@current_source.add_dependency_names(name)
- @specs[@current_spec.identifier] = @current_spec
+ @specs[@current_spec.full_name] = @current_spec
elsif spaces.size == 6
version = version.split(",").map(&:strip) if version
dep = Gem::Dependency.new(name, version)
diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1
index 20430a78a0..8549855b0d 100644
--- a/lib/bundler/man/bundle-add.1
+++ b/lib/bundler/man/bundle-add.1
@@ -1,13 +1,13 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-ADD" "1" "July 2022" "" ""
+.TH "BUNDLE\-ADD" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
.
.SH "SYNOPSIS"
-\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
+\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
.
.SH "DESCRIPTION"
Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
@@ -49,6 +49,10 @@ Specify the source for the added gem\.
Adds require path to gem\. Provide false, or a path as a string\.
.
.TP
+\fB\-\-path\fR
+Specify the file system path for the added gem\.
+.
+.TP
\fB\-\-git\fR
Specify the git source for the added gem\.
.
diff --git a/lib/bundler/man/bundle-add.1.ronn b/lib/bundler/man/bundle-add.1.ronn
index 8eead9c569..37c92e5fcd 100644
--- a/lib/bundler/man/bundle-add.1.ronn
+++ b/lib/bundler/man/bundle-add.1.ronn
@@ -3,7 +3,7 @@ bundle-add(1) -- Add gem to the Gemfile and run bundle install
## SYNOPSIS
-`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
+`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--path=PATH] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
## DESCRIPTION
Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`.
@@ -33,6 +33,9 @@ bundle add rails --group "development, test"
* `--require`, `-r`:
Adds require path to gem. Provide false, or a path as a string.
+* `--path`:
+ Specify the file system path for the added gem.
+
* `--git`:
Specify the git source for the added gem.
diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1
index 9c57c7c9c5..40c338916a 100644
--- a/lib/bundler/man/bundle-binstubs.1
+++ b/lib/bundler/man/bundle-binstubs.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-BINSTUBS" "1" "July 2022" "" ""
+.TH "BUNDLE\-BINSTUBS" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1
index fdaaa3b92a..69b1e1e3dd 100644
--- a/lib/bundler/man/bundle-cache.1
+++ b/lib/bundler/man/bundle-cache.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CACHE" "1" "July 2022" "" ""
+.TH "BUNDLE\-CACHE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
@@ -13,7 +13,7 @@
alias: \fBpackage\fR, \fBpack\fR
.
.SH "DESCRIPTION"
-Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running [bundle install(1)][bundle\-install], use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
+Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR, use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
.
.SH "GIT AND PATH GEMS"
The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR dependencies besides \.gem files\. This needs to be explicitly enabled via the \fB\-\-all\fR option\. Once used, the \fB\-\-all\fR option will be remembered\.
@@ -22,7 +22,7 @@ The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR depen
When using gems that have different packages for different platforms, Bundler supports caching of gems for other platforms where the Gemfile has been resolved (i\.e\. present in the lockfile) in \fBvendor/cache\fR\. This needs to be enabled via the \fB\-\-all\-platforms\fR option\. This setting will be remembered in your local bundler configuration\.
.
.SH "REMOTE FETCHING"
-By default, if you run \fBbundle install(1)\fR](bundle\-install\.1\.html) after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
+By default, if you run \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
.
.P
For instance, consider this Gemfile(5):
diff --git a/lib/bundler/man/bundle-cache.1.ronn b/lib/bundler/man/bundle-cache.1.ronn
index 46906d2b48..8112c2c551 100644
--- a/lib/bundler/man/bundle-cache.1.ronn
+++ b/lib/bundler/man/bundle-cache.1.ronn
@@ -10,7 +10,7 @@ alias: `package`, `pack`
## DESCRIPTION
Copy all of the `.gem` files needed to run the application into the
-`vendor/cache` directory. In the future, when running [bundle install(1)][bundle-install],
+`vendor/cache` directory. In the future, when running [`bundle install(1)`](bundle-install.1.html),
use the gems in the cache in preference to the ones on `rubygems.org`.
## GIT AND PATH GEMS
@@ -29,7 +29,7 @@ bundler configuration.
## REMOTE FETCHING
-By default, if you run `bundle install(1)`](bundle-install.1.html) after running
+By default, if you run [`bundle install(1)`](bundle-install.1.html) after running
[bundle cache(1)](bundle-cache.1.html), bundler will still connect to `rubygems.org`
to check whether a platform-specific gem exists for any of the gems
in `vendor/cache`.
diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1
index 5be7886274..748a37e7d1 100644
--- a/lib/bundler/man/bundle-check.1
+++ b/lib/bundler/man/bundle-check.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CHECK" "1" "July 2022" "" ""
+.TH "BUNDLE\-CHECK" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1
index 2bb95ed2de..af8f13cd89 100644
--- a/lib/bundler/man/bundle-clean.1
+++ b/lib/bundler/man/bundle-clean.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CLEAN" "1" "July 2022" "" ""
+.TH "BUNDLE\-CLEAN" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
@@ -20,5 +20,5 @@ Print the changes, but do not clean the unused gems\.
.
.TP
\fB\-\-force\fR
-Force a clean even if \fB\-\-path\fR is not set\.
+Forces cleaning up unused gems even if Bundler is configured to use globally installed gems\. As a consequence, removes all system gems except for the ones in the current application\.
diff --git a/lib/bundler/man/bundle-clean.1.ronn b/lib/bundler/man/bundle-clean.1.ronn
index de23991782..dae27c21ee 100644
--- a/lib/bundler/man/bundle-clean.1.ronn
+++ b/lib/bundler/man/bundle-clean.1.ronn
@@ -15,4 +15,4 @@ useful when you have made many changes to your gem dependencies.
* `--dry-run`:
Print the changes, but do not clean the unused gems.
* `--force`:
- Force a clean even if `--path` is not set.
+ Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application.
diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1
index 9f7887ca91..4442f33105 100644
--- a/lib/bundler/man/bundle-config.1
+++ b/lib/bundler/man/bundle-config.1
@@ -1,13 +1,22 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CONFIG" "1" "July 2022" "" ""
+.TH "BUNDLE\-CONFIG" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
.
.SH "SYNOPSIS"
-\fBbundle config\fR [list|get|set|unset] [\fIname\fR [\fIvalue\fR]]
+\fBbundle config\fR list
+.
+.br
+\fBbundle config\fR [get] NAME
+.
+.br
+\fBbundle config\fR [set] NAME VALUE
+.
+.br
+\fBbundle config\fR unset NAME
.
.SH "DESCRIPTION"
This command allows you to interact with Bundler\'s configuration system\.
@@ -30,7 +39,7 @@ Bundler default config
.IP "" 0
.
.P
-Executing \fBbundle config list\fR with will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
+Executing \fBbundle config list\fR will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
.
.P
Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\.
@@ -275,9 +284,6 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBssl_verify_mode\fR (\fBBUNDLE_SSL_VERIFY_MODE\fR): The SSL verification mode Bundler uses when making HTTPS requests\. Defaults to verify peer\.
.
.IP "\(bu" 4
-\fBsuppress_install_using_messages\fR (\fBBUNDLE_SUPPRESS_INSTALL_USING_MESSAGES\fR): Avoid printing \fBUsing \.\.\.\fR messages during installation when the version of a gem has not changed\.
-.
-.IP "\(bu" 4
\fBsystem_bindir\fR (\fBBUNDLE_SYSTEM_BINDIR\fR): The location where RubyGems installs binstubs\. Defaults to \fBGem\.bindir\fR\.
.
.IP "\(bu" 4
diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn
index 905c85fcd9..adc273ec62 100644
--- a/lib/bundler/man/bundle-config.1.ronn
+++ b/lib/bundler/man/bundle-config.1.ronn
@@ -3,7 +3,10 @@ bundle-config(1) -- Set bundler configuration options
## SYNOPSIS
-`bundle config` [list|get|set|unset] [<name> [<value>]]
+`bundle config` list<br>
+`bundle config` [get] NAME<br>
+`bundle config` [set] NAME VALUE<br>
+`bundle config` unset NAME
## DESCRIPTION
@@ -16,7 +19,7 @@ Bundler loads configuration settings in this order:
3. Global config (`~/.bundle/config`)
4. Bundler default config
-Executing `bundle config list` with will print a list of all bundler
+Executing `bundle config list` will print a list of all bundler
configuration for the current bundle, and where that configuration
was set.
@@ -262,9 +265,6 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `ssl_verify_mode` (`BUNDLE_SSL_VERIFY_MODE`):
The SSL verification mode Bundler uses when making HTTPS requests.
Defaults to verify peer.
-* `suppress_install_using_messages` (`BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES`):
- Avoid printing `Using ...` messages during installation when the version of
- a gem has not changed.
* `system_bindir` (`BUNDLE_SYSTEM_BINDIR`):
The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
* `timeout` (`BUNDLE_TIMEOUT`):
diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1
new file mode 100644
index 0000000000..24fff46cec
--- /dev/null
+++ b/lib/bundler/man/bundle-console.1
@@ -0,0 +1,53 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-CONSOLE" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded
+.
+.SH "SYNOPSIS"
+\fBbundle console\fR [GROUP]
+.
+.SH "DESCRIPTION"
+Starts an interactive Ruby console session in the context of the current bundle\.
+.
+.P
+If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\.
+.
+.P
+If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\.
+.
+.P
+The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following:
+.
+.IP "\(bu" 4
+\fBirb\fR (default)
+.
+.IP "\(bu" 4
+\fBpry\fR (https://github\.com/pry/pry)
+.
+.IP "\(bu" 4
+\fBripl\fR (https://github\.com/cldwalker/ripl)
+.
+.IP "" 0
+.
+.P
+\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\.
+.
+.SH "EXAMPLE"
+.
+.nf
+
+$ bundle config set console pry
+$ bundle console
+Resolving dependencies\.\.\.
+[1] pry(main)>
+.
+.fi
+.
+.SH "NOTES"
+This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem <NAME>\fR\.
+.
+.SH "SEE ALSO"
+Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR
diff --git a/lib/bundler/man/bundle-console.1.ronn b/lib/bundler/man/bundle-console.1.ronn
new file mode 100644
index 0000000000..f9096d386a
--- /dev/null
+++ b/lib/bundler/man/bundle-console.1.ronn
@@ -0,0 +1,44 @@
+bundle-console(1) -- Deprecated way to open an IRB session with the bundle pre-loaded
+=====================================================================================
+
+## SYNOPSIS
+
+`bundle console` [GROUP]
+
+## DESCRIPTION
+
+Starts an interactive Ruby console session in the context of the current bundle.
+
+If no `GROUP` is specified, all gems in the `default` group in the [Gemfile(5)](https://bundler.io/man/gemfile.5.html) are
+preliminarily loaded.
+
+If `GROUP` is specified, all gems in the given group in the Gemfile in addition
+to the gems in `default` group are loaded. Even if the given group does not
+exist in the Gemfile, IRB console starts without any warning or error.
+
+The environment variable `BUNDLE_CONSOLE` or `bundle config set console` can be used to change
+the shell from the following:
+
+* `irb` (default)
+* `pry` (https://github.com/pry/pry)
+* `ripl` (https://github.com/cldwalker/ripl)
+
+`bundle console` uses irb by default. An alternative Pry or Ripl can be used with
+`bundle console` by adjusting the `console` Bundler setting. Also make sure that
+`pry` or `ripl` is in your Gemfile.
+
+## EXAMPLE
+
+ $ bundle config set console pry
+ $ bundle console
+ Resolving dependencies...
+ [1] pry(main)>
+
+## NOTES
+
+This command was deprecated in Bundler 2.1 and will be removed in 3.0.
+Use `bin/console` script, which can be generated by `bundle gem <NAME>`.
+
+## SEE ALSO
+
+[Gemfile(5)](https://bundler.io/man/gemfile.5.html)
diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1
index 6aad9858db..57da8216cb 100644
--- a/lib/bundler/man/bundle-doctor.1
+++ b/lib/bundler/man/bundle-doctor.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-DOCTOR" "1" "July 2022" "" ""
+.TH "BUNDLE\-DOCTOR" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1
index 210dd178e8..852788db7a 100644
--- a/lib/bundler/man/bundle-exec.1
+++ b/lib/bundler/man/bundle-exec.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-EXEC" "1" "July 2022" "" ""
+.TH "BUNDLE\-EXEC" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
@@ -74,13 +74,13 @@ Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the l
By default, when attempting to \fBbundle exec\fR to a file with a ruby shebang, Bundler will \fBKernel\.load\fR that file instead of using \fBKernel\.exec\fR\. For the vast majority of cases, this is a performance improvement\. In a rare few cases, this could cause some subtle side\-effects (such as dependence on the exact contents of \fB$0\fR or \fB__FILE__\fR) and the optimization can be disabled by enabling the \fBdisable_exec_load\fR setting\.
.
.SS "Shelling out"
-Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_clean_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
+Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_unbundled_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
.
.IP "" 4
.
.nf
-Bundler\.with_clean_env do
+Bundler\.with_unbundled_env do
`brew install wget`
end
.
@@ -89,13 +89,13 @@ end
.IP "" 0
.
.P
-Using \fBwith_clean_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_clean_env\fR\.
+Using \fBwith_unbundled_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_unbundled_env\fR\.
.
.IP "" 4
.
.nf
-Bundler\.with_clean_env do
+Bundler\.with_unbundled_env do
Dir\.chdir "/other/bundler/project" do
`bundle exec \./script`
end
diff --git a/lib/bundler/man/bundle-exec.1.ronn b/lib/bundler/man/bundle-exec.1.ronn
index 5f5e78ed12..05948095e2 100644
--- a/lib/bundler/man/bundle-exec.1.ronn
+++ b/lib/bundler/man/bundle-exec.1.ronn
@@ -84,20 +84,20 @@ the `disable_exec_load` setting.
Any Ruby code that opens a subshell (like `system`, backticks, or `%x{}`) will
automatically use the current Bundler environment. If you need to shell out to
a Ruby command that is not part of your current bundle, use the
-`with_clean_env` method with a block. Any subshells created inside the block
+`with_unbundled_env` method with a block. Any subshells created inside the block
will be given the environment present before Bundler was activated. For
example, Homebrew commands run Ruby, but don't work inside a bundle:
- Bundler.with_clean_env do
+ Bundler.with_unbundled_env do
`brew install wget`
end
-Using `with_clean_env` is also necessary if you are shelling out to a different
+Using `with_unbundled_env` is also necessary if you are shelling out to a different
bundle. Any Bundler commands run in a subshell will inherit the current
Gemfile, so commands that need to run in the context of a different bundle also
-need to use `with_clean_env`.
+need to use `with_unbundled_env`.
- Bundler.with_clean_env do
+ Bundler.with_unbundled_env do
Dir.chdir "/other/bundler/project" do
`bundle exec ./script`
end
diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1
index 0fb6f7d0ab..8339b727ce 100644
--- a/lib/bundler/man/bundle-gem.1
+++ b/lib/bundler/man/bundle-gem.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-GEM" "1" "July 2022" "" ""
+.TH "BUNDLE\-GEM" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
@@ -31,41 +31,32 @@ The generated project skeleton can be customized with OPTIONS, as explained belo
.
.SH "OPTIONS"
.
-.TP
-\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR
-Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
+.IP "\(bu" 4
+\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR: Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
.
-.TP
-\fB\-\-no\-exe\fR
-Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-exe\fR: Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
.
-.TP
-\fB\-\-coc\fR
-Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
+.IP "\(bu" 4
+\fB\-\-coc\fR: Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-no\-coc\fR
-Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-coc\fR: Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
.
-.TP
-\fB\-\-ext\fR
-Add boilerplate for C extension code to the generated project\. This behavior is disabled by default\.
+.IP "\(bu" 4
+\fB\-\-ext=c\fR, \fB\-\-ext=rust\fR Add boilerplate for C or Rust (currently magnus \fIhttps://docs\.rs/magnus\fR based) extension code to the generated project\. This behavior is disabled by default\.
.
-.TP
-\fB\-\-no\-ext\fR
-Do not add C extension code (overrides \fB\-\-ext\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-ext\fR: Do not add extension code (overrides \fB\-\-ext\fR specified in the global config)\.
.
-.TP
-\fB\-\-mit\fR
-Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
+.IP "\(bu" 4
+\fB\-\-mit\fR: Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-no\-mit\fR
-Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
+.IP "\(bu" 4
+\fB\-\-no\-mit\fR: Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
.
-.TP
-\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR
-Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR: Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
.
.IP
When Bundler is configured to generate tests, this defaults to Bundler\'s global config setting \fBgem\.test\fR\.
@@ -76,9 +67,8 @@ When Bundler is configured to not generate tests, an interactive prompt will be
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=travis\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR
-Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBtravis\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR: Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to generate CI files, this defaults to Bundler\'s global config setting \fBgem\.ci\fR\.
@@ -89,9 +79,8 @@ When Bundler is configured to not generate CI files, an interactive prompt will
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR
-Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
+.IP "\(bu" 4
+\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\.
@@ -102,9 +91,10 @@ When Bundler is configured not to add a linter, an interactive prompt will be di
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
-.TP
-\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR
-Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
+.IP "\(bu" 4
+\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR: Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
+.
+.IP "" 0
.
.SH "SEE ALSO"
.
diff --git a/lib/bundler/man/bundle-gem.1.ronn b/lib/bundler/man/bundle-gem.1.ronn
index 61c741fb24..46fa2f179f 100644
--- a/lib/bundler/man/bundle-gem.1.ronn
+++ b/lib/bundler/man/bundle-gem.1.ronn
@@ -41,12 +41,12 @@ configuration file using the following names:
Do not create a `CODE_OF_CONDUCT.md` (overrides `--coc` specified in the
global config).
-* `--ext`:
- Add boilerplate for C extension code to the generated project. This behavior
+* `--ext=c`, `--ext=rust`
+ Add boilerplate for C or Rust (currently [magnus](https://docs.rs/magnus) based) extension code to the generated project. This behavior
is disabled by default.
* `--no-ext`:
- Do not add C extension code (overrides `--ext` specified in the global
+ Do not add extension code (overrides `--ext` specified in the global
config).
* `--mit`:
@@ -76,9 +76,9 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
-* `--ci`, `--ci=github`, `--ci=travis`, `--ci=gitlab`, `--ci=circle`:
+* `--ci`, `--ci=github`, `--ci=gitlab`, `--ci=circle`:
Specify the continuous integration service that Bundler should use when
- generating the project. Acceptable values are `github`, `travis`, `gitlab`
+ generating the project. Acceptable values are `github`, `gitlab`
and `circle`. A configuration file will be generated in the project directory.
Given no option is specified:
diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1
new file mode 100644
index 0000000000..9787c2d49f
--- /dev/null
+++ b/lib/bundler/man/bundle-help.1
@@ -0,0 +1,13 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-HELP" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-help\fR \- Displays detailed help for each subcommand
+.
+.SH "SYNOPSIS"
+\fBbundle help\fR [COMMAND]
+.
+.SH "DESCRIPTION"
+Displays detailed help for the given subcommand\. You can specify a single \fBCOMMAND\fR at the same time\. When \fBCOMMAND\fR is omitted, help for \fBhelp\fR command will be displayed\.
diff --git a/lib/bundler/man/bundle-help.1.ronn b/lib/bundler/man/bundle-help.1.ronn
new file mode 100644
index 0000000000..0e144aead7
--- /dev/null
+++ b/lib/bundler/man/bundle-help.1.ronn
@@ -0,0 +1,12 @@
+bundle-help(1) -- Displays detailed help for each subcommand
+============================================================
+
+## SYNOPSIS
+
+`bundle help` [COMMAND]
+
+## DESCRIPTION
+
+Displays detailed help for the given subcommand.
+You can specify a single `COMMAND` at the same time.
+When `COMMAND` is omitted, help for `help` command will be displayed.
diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1
index d3bad843b8..2cced71520 100644
--- a/lib/bundler/man/bundle-info.1
+++ b/lib/bundler/man/bundle-info.1
@@ -1,16 +1,16 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INFO" "1" "July 2022" "" ""
+.TH "BUNDLE\-INFO" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-info\fR \- Show information for the given gem in your bundle
.
.SH "SYNOPSIS"
-\fBbundle info\fR [GEM] [\-\-path]
+\fBbundle info\fR [GEM_NAME] [\-\-path]
.
.SH "DESCRIPTION"
-Print the basic information about the provided GEM such as homepage, version, path and summary\.
+Given a gem name present in your bundle, print the basic information about it such as homepage, version, path and summary\.
.
.SH "OPTIONS"
.
diff --git a/lib/bundler/man/bundle-info.1.ronn b/lib/bundler/man/bundle-info.1.ronn
index 47e457aa3c..cecdeb564f 100644
--- a/lib/bundler/man/bundle-info.1.ronn
+++ b/lib/bundler/man/bundle-info.1.ronn
@@ -3,13 +3,13 @@ bundle-info(1) -- Show information for the given gem in your bundle
## SYNOPSIS
-`bundle info` [GEM]
+`bundle info` [GEM_NAME]
[--path]
## DESCRIPTION
-Print the basic information about the provided GEM such as homepage, version,
-path and summary.
+Given a gem name present in your bundle, print the basic information about it
+ such as homepage, version, path and summary.
## OPTIONS
diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1
index 0b3abfeefc..c7a9a155b5 100644
--- a/lib/bundler/man/bundle-init.1
+++ b/lib/bundler/man/bundle-init.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INIT" "1" "July 2022" "" ""
+.TH "BUNDLE\-INIT" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
@@ -18,6 +18,10 @@ Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working d
\fB\-\-gemspec\fR
Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)]
.
+.TP
+\fB\-\-gemfile\fR
+Use the specified name for the gemfile instead of \fBGemfile\fR
+.
.SH "FILES"
Included in the default [\fBGemfile(5)\fR][Gemfile(5)] generated is the line \fB# frozen_string_literal: true\fR\. This is a magic comment supported for the first time in Ruby 2\.3\. The presence of this line results in all string literals in the file being implicitly frozen\.
.
diff --git a/lib/bundler/man/bundle-init.1.ronn b/lib/bundler/man/bundle-init.1.ronn
index 9d3d97deea..7d3cede1f6 100644
--- a/lib/bundler/man/bundle-init.1.ronn
+++ b/lib/bundler/man/bundle-init.1.ronn
@@ -16,6 +16,8 @@ created [`Gemfile(5)`][Gemfile(5)].
* `--gemspec`:
Use the specified .gemspec to create the [`Gemfile(5)`][Gemfile(5)]
+* `--gemfile`:
+ Use the specified name for the gemfile instead of `Gemfile`
## FILES
diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1
index 53f2eaae0a..9e25c29085 100644
--- a/lib/bundler/man/bundle-inject.1
+++ b/lib/bundler/man/bundle-inject.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INJECT" "1" "July 2022" "" ""
+.TH "BUNDLE\-INJECT" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1
index 8e94fe2437..337683af06 100644
--- a/lib/bundler/man/bundle-install.1
+++ b/lib/bundler/man/bundle-install.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INSTALL" "1" "July 2022" "" ""
+.TH "BUNDLE\-INSTALL" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
@@ -170,35 +170,6 @@ As a result, \fBbundle install \-\-deployment\fR installs gems to the \fBvendor/
.
.IP "" 0
.
-.SH "SUDO USAGE"
-By default, Bundler installs gems to the same location as \fBgem install\fR\.
-.
-.P
-In some cases, that location may not be writable by your Unix user\. In that case, Bundler will stage everything in a temporary directory, then ask you for your \fBsudo\fR password in order to copy the gems into their system location\.
-.
-.P
-From your perspective, this is identical to installing the gems directly into the system\.
-.
-.P
-You should never use \fBsudo bundle install\fR\. This is because several other steps in \fBbundle install\fR must be performed as the current user:
-.
-.IP "\(bu" 4
-Updating your \fBGemfile\.lock\fR
-.
-.IP "\(bu" 4
-Updating your \fBvendor/cache\fR, if necessary
-.
-.IP "\(bu" 4
-Checking out private git repositories using your user\'s SSH keys
-.
-.IP "" 0
-.
-.P
-Of these three, the first two could theoretically be performed by \fBchown\fRing the resulting files to \fB$SUDO_USER\fR\. The third, however, can only be performed by invoking the \fBgit\fR command as the current user\. Therefore, git gems are downloaded and installed into \fB~/\.bundle\fR rather than $GEM_HOME or $BUNDLE_PATH\.
-.
-.P
-As a result, you should run \fBbundle install\fR as the current user, and Bundler will ask for your password if it is needed to put the gems into their final location\.
-.
.SH "INSTALLING GROUPS"
By default, \fBbundle install\fR will install all gems in all groups in your Gemfile(5), except those declared for a different platform\.
.
diff --git a/lib/bundler/man/bundle-install.1.ronn b/lib/bundler/man/bundle-install.1.ronn
index 47200ac2d5..be9ed0f974 100644
--- a/lib/bundler/man/bundle-install.1.ronn
+++ b/lib/bundler/man/bundle-install.1.ronn
@@ -224,35 +224,6 @@ will cause an error when the Gemfile(5) is modified.
the `vendor/bundle` directory in the application. This may be
overridden using the `--path` option.
-## SUDO USAGE
-
-By default, Bundler installs gems to the same location as `gem install`.
-
-In some cases, that location may not be writable by your Unix user. In
-that case, Bundler will stage everything in a temporary directory,
-then ask you for your `sudo` password in order to copy the gems into
-their system location.
-
-From your perspective, this is identical to installing the gems
-directly into the system.
-
-You should never use `sudo bundle install`. This is because several
-other steps in `bundle install` must be performed as the current user:
-
-* Updating your `Gemfile.lock`
-* Updating your `vendor/cache`, if necessary
-* Checking out private git repositories using your user's SSH keys
-
-Of these three, the first two could theoretically be performed by
-`chown`ing the resulting files to `$SUDO_USER`. The third, however,
-can only be performed by invoking the `git` command as
-the current user. Therefore, git gems are downloaded and installed
-into `~/.bundle` rather than $GEM_HOME or $BUNDLE_PATH.
-
-As a result, you should run `bundle install` as the current user,
-and Bundler will ask for your password if it is needed to put the
-gems into their final location.
-
## INSTALLING GROUPS
By default, `bundle install` will install all gems in all groups
diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1
index 3a9cc9a237..1680e6007a 100644
--- a/lib/bundler/man/bundle-list.1
+++ b/lib/bundler/man/bundle-list.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LIST" "1" "July 2022" "" ""
+.TH "BUNDLE\-LIST" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-list\fR \- List all the gems in the bundle
diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1
index ac03c5478e..8722c44b3d 100644
--- a/lib/bundler/man/bundle-lock.1
+++ b/lib/bundler/man/bundle-lock.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LOCK" "1" "July 2022" "" ""
+.TH "BUNDLE\-LOCK" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1
index be6c5af248..3513f0d09b 100644
--- a/lib/bundler/man/bundle-open.1
+++ b/lib/bundler/man/bundle-open.1
@@ -1,13 +1,13 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OPEN" "1" "July 2022" "" ""
+.TH "BUNDLE\-OPEN" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
.
.SH "SYNOPSIS"
-\fBbundle open\fR [GEM]
+\fBbundle open\fR [GEM] [\-\-path=PATH]
.
.SH "DESCRIPTION"
Opens the source directory of the provided GEM in your editor\.
@@ -30,3 +30,23 @@ bundle open \'rack\'
.
.P
Will open the source directory for the \'rack\' gem in your bundle\.
+.
+.IP "" 4
+.
+.nf
+
+bundle open \'rack\' \-\-path \'README\.md\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Will open the README\.md file of the \'rack\' gem source in your bundle\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-path\fR
+Specify GEM source relative path to open\.
+
diff --git a/lib/bundler/man/bundle-open.1.ronn b/lib/bundler/man/bundle-open.1.ronn
index 497beac93f..a857f3a965 100644
--- a/lib/bundler/man/bundle-open.1.ronn
+++ b/lib/bundler/man/bundle-open.1.ronn
@@ -3,7 +3,7 @@ bundle-open(1) -- Opens the source directory for a gem in your bundle
## SYNOPSIS
-`bundle open` [GEM]
+`bundle open` [GEM] [--path=PATH]
## DESCRIPTION
@@ -17,3 +17,11 @@ Example:
bundle open 'rack'
Will open the source directory for the 'rack' gem in your bundle.
+
+ bundle open 'rack' --path 'README.md'
+
+Will open the README.md file of the 'rack' gem source in your bundle.
+
+## OPTIONS
+* `--path`:
+ Specify GEM source relative path to open.
diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1
index fc3d5e8caf..129ff00f58 100644
--- a/lib/bundler/man/bundle-outdated.1
+++ b/lib/bundler/man/bundle-outdated.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OUTDATED" "1" "July 2022" "" ""
+.TH "BUNDLE\-OUTDATED" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-outdated\fR \- List installed gems with newer versions available
@@ -83,9 +83,10 @@ If the regular output shows the following:
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.
.fi
.
@@ -98,7 +99,8 @@ If the regular output shows the following:
.
.nf
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
+* Gem Current Latest Requested Groups
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
.
.fi
.
@@ -111,7 +113,8 @@ If the regular output shows the following:
.
.nf
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.
.fi
.
@@ -124,7 +127,8 @@ If the regular output shows the following:
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.
.fi
.
@@ -137,8 +141,8 @@ Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patc
.
.nf
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.
.fi
.
diff --git a/lib/bundler/man/bundle-outdated.1.ronn b/lib/bundler/man/bundle-outdated.1.ronn
index 04096ffbb6..27bf21ab9d 100644
--- a/lib/bundler/man/bundle-outdated.1.ronn
+++ b/lib/bundler/man/bundle-outdated.1.ronn
@@ -78,25 +78,28 @@ in the output.
If the regular output shows the following:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-major` would only show:
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+ * Gem Current Latest Requested Groups
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
`--filter-minor` would only show:
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-patch` would only show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Filter options can be combined. `--filter-minor` and `--filter-patch` would show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
-
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Combining all three `filter` options would be the same result as providing none of them.
diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1
index f5a90c59e9..5021c46b4c 100644
--- a/lib/bundler/man/bundle-platform.1
+++ b/lib/bundler/man/bundle-platform.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PLATFORM" "1" "July 2022" "" ""
+.TH "BUNDLE\-PLATFORM" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-platform\fR \- Displays platform compatibility information
@@ -65,7 +65,7 @@ It will display the ruby directive information, so you don\'t have to parse it f
.SH "SEE ALSO"
.
.IP "\(bu" 4
-bundle\-lock(1) \fIbundle\-lock\.1\.ronn\fR
+bundle\-lock(1) \fIbundle\-lock\.1\.html\fR
.
.IP "" 0
diff --git a/lib/bundler/man/bundle-platform.1.ronn b/lib/bundler/man/bundle-platform.1.ronn
index eb9baa1c62..744acd1b43 100644
--- a/lib/bundler/man/bundle-platform.1.ronn
+++ b/lib/bundler/man/bundle-platform.1.ronn
@@ -46,4 +46,4 @@ match the running Ruby VM, it tells you what part does not.
## SEE ALSO
-* [bundle-lock(1)](bundle-lock.1.ronn)
+* [bundle-lock(1)](bundle-lock.1.html)
diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1
new file mode 100644
index 0000000000..ec30e1d0fd
--- /dev/null
+++ b/lib/bundler/man/bundle-plugin.1
@@ -0,0 +1,81 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-PLUGIN" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-plugin\fR \- Manage Bundler plugins
+.
+.SH "SYNOPSIS"
+\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git|\-\-local_git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR]
+.
+.br
+\fBbundle plugin\fR uninstall PLUGINS
+.
+.br
+\fBbundle plugin\fR list
+.
+.br
+\fBbundle plugin\fR help [COMMAND]
+.
+.SH "DESCRIPTION"
+You can install, uninstall, and list plugin(s) with this command to extend functionalities of Bundler\.
+.
+.SH "SUB\-COMMANDS"
+.
+.SS "install"
+Install the given plugin(s)\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph\fR: Install bundler\-graph gem from RubyGems\.org\. The global source, specified in source in Gemfile is ignored\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR: Install bundler\-graph gem from example\.com\. The global source, specified in source in Gemfile is not considered\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR: You can specify the version of the gem via \fB\-\-version\fR\.
+.
+.IP "\(bu" 4
+\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR: Install bundler\-graph gem from Git repository\. \fB\-\-git\fR can be replaced with \fB\-\-local\-git\fR\. You cannot use both \fB\-\-git\fR and \fB\-\-local\-git\fR\. You can use standard Git URLs like:
+.
+.IP "\(bu" 4
+\fBssh://[user@]host\.xz[:port]/path/to/repo\.git\fR
+.
+.IP "\(bu" 4
+\fBhttp[s]://host\.xz[:port]/path/to/repo\.git\fR
+.
+.IP "\(bu" 4
+\fB/path/to/repo\fR
+.
+.IP "\(bu" 4
+\fBfile:///path/to/repo\fR
+.
+.IP "" 0
+.
+.IP
+When you specify \fB\-\-git\fR/\fB\-\-local\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\. When you specify both, only the latter is used\.
+.
+.IP "" 0
+.
+.SS "uninstall"
+Uninstall the plugin(s) specified in PLUGINS\.
+.
+.SS "list"
+List the installed plugins and available commands\.
+.
+.P
+No options\.
+.
+.SS "help"
+Describe subcommands or one specific subcommand\.
+.
+.P
+No options\.
+.
+.SH "SEE ALSO"
+.
+.IP "\(bu" 4
+How to write a Bundler plugin \fIhttps://bundler\.io/guides/bundler_plugins\.html\fR
+.
+.IP "" 0
+
diff --git a/lib/bundler/man/bundle-plugin.1.ronn b/lib/bundler/man/bundle-plugin.1.ronn
new file mode 100644
index 0000000000..4f234eeba7
--- /dev/null
+++ b/lib/bundler/man/bundle-plugin.1.ronn
@@ -0,0 +1,59 @@
+bundle-plugin(1) -- Manage Bundler plugins
+==========================================
+
+## SYNOPSIS
+
+`bundle plugin` install PLUGINS [--source=<SOURCE>] [--version=<version>]
+ [--git|--local_git=<git-url>] [--branch=<branch>|--ref=<rev>]<br>
+`bundle plugin` uninstall PLUGINS<br>
+`bundle plugin` list<br>
+`bundle plugin` help [COMMAND]
+
+## DESCRIPTION
+
+You can install, uninstall, and list plugin(s) with this command to extend functionalities of Bundler.
+
+## SUB-COMMANDS
+
+### install
+
+Install the given plugin(s).
+
+* `bundle plugin install bundler-graph`:
+ Install bundler-graph gem from RubyGems.org. The global source, specified in source in Gemfile is ignored.
+
+* `bundle plugin install bundler-graph --source https://example.com`:
+ Install bundler-graph gem from example.com. The global source, specified in source in Gemfile is not considered.
+
+* `bundle plugin install bundler-graph --version 0.2.1`:
+ You can specify the version of the gem via `--version`.
+
+* `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph`:
+ Install bundler-graph gem from Git repository. `--git` can be replaced with `--local-git`. You cannot use both `--git` and `--local-git`. You can use standard Git URLs like:
+
+ * `ssh://[user@]host.xz[:port]/path/to/repo.git`
+ * `http[s]://host.xz[:port]/path/to/repo.git`
+ * `/path/to/repo`
+ * `file:///path/to/repo`
+
+ When you specify `--git`/`--local-git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use. When you specify both, only the latter is used.
+
+### uninstall
+
+Uninstall the plugin(s) specified in PLUGINS.
+
+### list
+
+List the installed plugins and available commands.
+
+No options.
+
+### help
+
+Describe subcommands or one specific subcommand.
+
+No options.
+
+## SEE ALSO
+
+* [How to write a Bundler plugin](https://bundler.io/guides/bundler_plugins.html)
diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1
index 44e5a83a01..af81c48d2b 100644
--- a/lib/bundler/man/bundle-pristine.1
+++ b/lib/bundler/man/bundle-pristine.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PRISTINE" "1" "July 2022" "" ""
+.TH "BUNDLE\-PRISTINE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1
index 29ec246018..d86cf134bd 100644
--- a/lib/bundler/man/bundle-remove.1
+++ b/lib/bundler/man/bundle-remove.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-REMOVE" "1" "July 2022" "" ""
+.TH "BUNDLE\-REMOVE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-remove\fR \- Removes gems from the Gemfile
diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1
index 227b1c8a1e..aa91176bf2 100644
--- a/lib/bundler/man/bundle-show.1
+++ b/lib/bundler/man/bundle-show.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-SHOW" "1" "July 2022" "" ""
+.TH "BUNDLE\-SHOW" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1
index 7d0988bfa5..e4e10ad23b 100644
--- a/lib/bundler/man/bundle-update.1
+++ b/lib/bundler/man/bundle-update.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-UPDATE" "1" "July 2022" "" ""
+.TH "BUNDLE\-UPDATE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-update\fR \- Update your gems to the latest available versions
diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1
new file mode 100644
index 0000000000..5e3ed44600
--- /dev/null
+++ b/lib/bundler/man/bundle-version.1
@@ -0,0 +1,35 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-VERSION" "1" "August 2023" "" ""
+.
+.SH "NAME"
+\fBbundle\-version\fR \- Prints Bundler version information
+.
+.SH "SYNOPSIS"
+\fBbundle version\fR
+.
+.SH "DESCRIPTION"
+Prints Bundler version information\.
+.
+.SH "OPTIONS"
+No options\.
+.
+.SH "EXAMPLE"
+Print the version of Bundler with build date and commit hash of the in the Git source\.
+.
+.IP "" 4
+.
+.nf
+
+bundle version
+.
+.fi
+.
+.IP "" 0
+.
+.P
+shows \fBBundler version 2\.3\.21 (2022\-08\-24 commit d54be5fdd8)\fR for example\.
+.
+.P
+cf\. \fBbundle \-\-version\fR shows \fBBundler version 2\.3\.21\fR\.
diff --git a/lib/bundler/man/bundle-version.1.ronn b/lib/bundler/man/bundle-version.1.ronn
new file mode 100644
index 0000000000..46c6f0b30a
--- /dev/null
+++ b/lib/bundler/man/bundle-version.1.ronn
@@ -0,0 +1,24 @@
+bundle-version(1) -- Prints Bundler version information
+=======================================================
+
+## SYNOPSIS
+
+`bundle version`
+
+## DESCRIPTION
+
+Prints Bundler version information.
+
+## OPTIONS
+
+No options.
+
+## EXAMPLE
+
+Print the version of Bundler with build date and commit hash of the in the Git source.
+
+ bundle version
+
+shows `Bundler version 2.3.21 (2022-08-24 commit d54be5fdd8)` for example.
+
+cf. `bundle --version` shows `Bundler version 2.3.21`.
diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1
index f6f51cde0e..d5330ec754 100644
--- a/lib/bundler/man/bundle-viz.1
+++ b/lib/bundler/man/bundle-viz.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-VIZ" "1" "July 2022" "" ""
+.TH "BUNDLE\-VIZ" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1
index f683e78cc6..99c65a72b5 100644
--- a/lib/bundler/man/bundle.1
+++ b/lib/bundler/man/bundle.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE" "1" "July 2022" "" ""
+.TH "BUNDLE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\fR \- Ruby Dependency Management
@@ -55,7 +55,7 @@ Execute a script in the current bundle
Specify and read configuration options for Bundler
.
.TP
-\fBbundle help(1)\fR
+\fBbundle help(1)\fR \fIbundle\-help\.1\.html\fR
Display detailed help for each subcommand
.
.SH "UTILITIES"
@@ -120,6 +120,14 @@ Display warnings about common problems
\fBbundle remove(1)\fR \fIbundle\-remove\.1\.html\fR
Removes gems from the Gemfile
.
+.TP
+\fBbundle plugin(1)\fR \fIbundle\-plugin\.1\.html\fR
+Manage Bundler plugins
+.
+.TP
+\fBbundle version(1)\fR \fIbundle\-version\.1\.html\fR
+Prints Bundler version information
+.
.SH "PLUGINS"
When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-<command>\fR and execute it, passing down any extra arguments to it\.
.
diff --git a/lib/bundler/man/bundle.1.ronn b/lib/bundler/man/bundle.1.ronn
index 8f0159eee5..8245effabd 100644
--- a/lib/bundler/man/bundle.1.ronn
+++ b/lib/bundler/man/bundle.1.ronn
@@ -46,7 +46,7 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle config(1)`](bundle-config.1.html):
Specify and read configuration options for Bundler
-* `bundle help(1)`:
+* [`bundle help(1)`](bundle-help.1.html):
Display detailed help for each subcommand
## UTILITIES
@@ -97,6 +97,12 @@ We divide `bundle` subcommands into primary commands and utilities:
* [`bundle remove(1)`](bundle-remove.1.html):
Removes gems from the Gemfile
+* [`bundle plugin(1)`](bundle-plugin.1.html):
+ Manage Bundler plugins
+
+* [`bundle version(1)`](bundle-version.1.html):
+ Prints Bundler version information
+
## PLUGINS
When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES,
diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5
index 63a16ca3ff..352fa0f545 100644
--- a/lib/bundler/man/gemfile.5
+++ b/lib/bundler/man/gemfile.5
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "GEMFILE" "5" "July 2022" "" ""
+.TH "GEMFILE" "5" "August 2023" "" ""
.
.SH "NAME"
\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
@@ -73,13 +73,26 @@ Credentials in the source URL will take precedence over credentials set using \f
If your application requires a specific Ruby version or engine, specify your requirements using the \fBruby\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\.
.
.SS "VERSION (required)"
-The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, Rubinius or TruffleRuby, this should be the Ruby version that the engine is compatible with\.
+The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, TruffleRuby, etc\., this should be the Ruby version that the engine is compatible with\.
.
.IP "" 4
.
.nf
-ruby "1\.9\.3"
+ruby "3\.1\.2"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If you wish to derive your Ruby version from a version file (ie \.ruby\-version), you can use the \fBfile\fR option instead\.
+.
+.IP "" 4
+.
+.nf
+
+ruby file: "\.ruby\-version"
.
.fi
.
@@ -95,7 +108,7 @@ What exactly is an Engine? \- A Ruby engine is an implementation of the Ruby lan
For background: the reference or original implementation of the Ruby programming language is called Matz\'s Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\.
.
.IP "\(bu" 4
-Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include Rubinius \fIhttps://rubinius\.com/\fR, and JRuby \fIhttp://jruby\.org/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\.
+Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttp://jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
.
.IP "" 0
.
@@ -106,20 +119,23 @@ Each application \fImay\fR specify a Ruby engine version\. If an engine version
.
.nf
-ruby "1\.8\.7", engine: "jruby", engine_version: "1\.6\.7"
+ruby "2\.6\.8", engine: "jruby", engine_version: "9\.3\.8\.0"
.
.fi
.
.IP "" 0
.
.SS "PATCHLEVEL"
-Each application \fImay\fR specify a Ruby patchlevel\.
+Each application \fImay\fR specify a Ruby patchlevel\. Specifying the patchlevel has been meaningless since Ruby 2\.1\.0 was released as the patchlevel is now uniquely determined by a combination of major, minor, and teeny version numbers\.
+.
+.P
+This option was implemented in Bundler 1\.4\.0 for Ruby 2\.0 or earlier\.
.
.IP "" 4
.
.nf
-ruby "2\.0\.0", patchlevel: "247"
+ruby "3\.1\.2", patchlevel: "20"
.
.fi
.
@@ -254,19 +270,23 @@ There are a number of \fBGemfile\fR platforms:
.
.TP
\fBruby\fR
-C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows
+C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows
.
.TP
\fBmri\fR
-Same as \fIruby\fR, but only C Ruby (MRI)
+C Ruby (MRI) only, but not Windows
.
.TP
-\fBmingw\fR
-Windows 32 bit \'mingw32\' platform (aka RubyInstaller)
+\fBwindows\fR
+Windows C Ruby (MRI), including RubyInstaller 32\-bit and 64\-bit versions
.
.TP
-\fBx64_mingw\fR
-Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64)
+\fBmswin\fR
+Windows C Ruby (MRI), including RubyInstaller 32\-bit versions
+.
+.TP
+\fBmswin64\fR
+Windows C Ruby (MRI), including RubyInstaller 64\-bit versions
.
.TP
\fBrbx\fR
@@ -280,55 +300,29 @@ JRuby
\fBtruffleruby\fR
TruffleRuby
.
-.TP
-\fBmswin\fR
-Windows
-.
-.P
-You can restrict further by platform and version for all platforms \fIexcept\fR for \fBrbx\fR, \fBjruby\fR, \fBtruffleruby\fR and \fBmswin\fR\.
-.
.P
-To specify a version in addition to a platform, append the version number without the delimiter to the platform\. For example, to specify that a gem should only be used on platforms with Ruby 2\.3, use:
+On platforms \fBruby\fR, \fBmri\fR, \fBmswin\fR, \fBmswin64\fR, and \fBwindows\fR, you may additionally specify a version by appending the major and minor version numbers without a delimiter\. For example, to specify that a gem should only be used on platform \fBruby\fR version 3\.1, use:
.
.IP "" 4
.
.nf
-ruby_23
+ruby_31
.
.fi
.
.IP "" 0
.
.P
-The full list of platforms and supported versions includes:
-.
-.TP
-\fBruby\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBmri\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBmingw\fR
-1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.TP
-\fBx64_mingw\fR
-2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6
-.
-.P
-As with groups, you can specify one or more platforms:
+As with groups (above), you may specify one or more platforms:
.
.IP "" 4
.
.nf
gem "weakling", platforms: :jruby
-gem "ruby\-debug", platforms: :mri_18
-gem "nokogiri", platforms: [:mri_18, :jruby]
+gem "ruby\-debug", platforms: :mri_31
+gem "nokogiri", platforms: [:windows_31, :jruby]
.
.fi
.
@@ -527,7 +521,7 @@ Are both equivalent to
.
.nf
-gem "rails", git: "git://github\.com/rails/rails\.git"
+gem "rails", git: "https://github\.com/rails/rails\.git"
.
.fi
.
@@ -721,7 +715,7 @@ If you wish to use Bundler to help install dependencies for a gem while it is be
The \fBgemspec\fR method adds any runtime dependencies as gem requirements in the default group\. It also adds development dependencies as gem requirements in the \fBdevelopment\fR group\. Finally, it adds a gem requirement on your project (\fBpath: \'\.\'\fR)\. In conjunction with \fBBundler\.setup\fR, this allows you to require project files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths\.
.
.P
-The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: "{,\fI,\fR/*}\.gemspec"), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
+The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: \fB{,*,*/*}\.gemspec\fR), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
.
.P
When a \fBgemspec\fR dependency encounters version conflicts during resolution, the local version under development will always be selected \-\- even if there are remote versions that better match other requirements for the \fBgemspec\fR gem\.
diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn
index c2f9141c65..6749c33f59 100644
--- a/lib/bundler/man/gemfile.5.ronn
+++ b/lib/bundler/man/gemfile.5.ronn
@@ -64,10 +64,15 @@ All parameters are `OPTIONAL` unless otherwise specified.
### VERSION (required)
The version of Ruby that your application requires. If your application
-requires an alternate Ruby engine, such as JRuby, Rubinius or TruffleRuby, this
+requires an alternate Ruby engine, such as JRuby, TruffleRuby, etc., this
should be the Ruby version that the engine is compatible with.
- ruby "1.9.3"
+ ruby "3.1.2"
+
+If you wish to derive your Ruby version from a version file (ie .ruby-version),
+you can use the `file` option instead.
+
+ ruby file: ".ruby-version"
### ENGINE
@@ -86,9 +91,10 @@ What exactly is an Engine?
- [Other implementations](https://www.ruby-lang.org/en/about/) of Ruby exist.
Some of the more well-known implementations include
- [Rubinius](https://rubinius.com/), and [JRuby](http://jruby.org/).
+ [JRuby](http://jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
Rubinius is an alternative implementation of Ruby written in Ruby.
JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine.
+ TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM.
### ENGINE VERSION
@@ -96,13 +102,17 @@ Each application _may_ specify a Ruby engine version. If an engine version is
specified, an engine _must_ also be specified. If the engine is "ruby" the
engine version specified _must_ match the Ruby version.
- ruby "1.8.7", engine: "jruby", engine_version: "1.6.7"
+ ruby "2.6.8", engine: "jruby", engine_version: "9.3.8.0"
### PATCHLEVEL
-Each application _may_ specify a Ruby patchlevel.
+Each application _may_ specify a Ruby patchlevel. Specifying the patchlevel has
+been meaningless since Ruby 2.1.0 was released as the patchlevel is now
+uniquely determined by a combination of major, minor, and teeny version numbers.
+
+This option was implemented in Bundler 1.4.0 for Ruby 2.0 or earlier.
- ruby "2.0.0", patchlevel: "247"
+ ruby "3.1.2", patchlevel: "20"
## GEMS
@@ -190,47 +200,34 @@ platforms.
There are a number of `Gemfile` platforms:
* `ruby`:
- C Ruby (MRI), Rubinius or TruffleRuby, but `NOT` Windows
+ C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows
* `mri`:
- Same as _ruby_, but only C Ruby (MRI)
- * `mingw`:
- Windows 32 bit 'mingw32' platform (aka RubyInstaller)
- * `x64_mingw`:
- Windows 64 bit 'mingw32' platform (aka RubyInstaller x64)
+ C Ruby (MRI) only, but not Windows
+ * `windows`:
+ Windows C Ruby (MRI), including RubyInstaller 32-bit and 64-bit versions
+ * `mswin`:
+ Windows C Ruby (MRI), including RubyInstaller 32-bit versions
+ * `mswin64`:
+ Windows C Ruby (MRI), including RubyInstaller 64-bit versions
* `rbx`:
Rubinius
* `jruby`:
JRuby
* `truffleruby`:
TruffleRuby
- * `mswin`:
- Windows
-
-You can restrict further by platform and version for all platforms *except* for
-`rbx`, `jruby`, `truffleruby` and `mswin`.
-To specify a version in addition to a platform, append the version number without
-the delimiter to the platform. For example, to specify that a gem should only be
-used on platforms with Ruby 2.3, use:
+On platforms `ruby`, `mri`, `mswin`, `mswin64`, and `windows`, you may
+additionally specify a version by appending the major and minor version numbers
+without a delimiter. For example, to specify that a gem should only be used on
+platform `ruby` version 3.1, use:
- ruby_23
+ ruby_31
-The full list of platforms and supported versions includes:
-
- * `ruby`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `mri`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `mingw`:
- 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
- * `x64_mingw`:
- 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
-
-As with groups, you can specify one or more platforms:
+As with groups (above), you may specify one or more platforms:
gem "weakling", platforms: :jruby
- gem "ruby-debug", platforms: :mri_18
- gem "nokogiri", platforms: [:mri_18, :jruby]
+ gem "ruby-debug", platforms: :mri_31
+ gem "nokogiri", platforms: [:windows_31, :jruby]
All operations involving groups ([`bundle install`](bundle-install.1.html), `Bundler.setup`,
`Bundler.require`) behave exactly the same as if any groups not
@@ -387,7 +384,7 @@ same, you can omit one.
Are both equivalent to
- gem "rails", git: "git://github.com/rails/rails.git"
+ gem "rails", git: "https://github.com/rails/rails.git"
Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument.
@@ -522,7 +519,7 @@ paths.
The `gemspec` method supports optional `:path`, `:glob`, `:name`, and `:development_group`
options, which control where bundler looks for the `.gemspec`, the glob it uses to look
-for the gemspec (defaults to: "{,*,*/*}.gemspec"), what named `.gemspec` it uses
+for the gemspec (defaults to: `{,*,*/*}.gemspec`), what named `.gemspec` it uses
(if more than one is present), and which group development dependencies are included in.
When a `gemspec` dependency encounters version conflicts during resolution, the
diff --git a/lib/bundler/man/index.txt b/lib/bundler/man/index.txt
index ef2956b2f9..24f7633e66 100644
--- a/lib/bundler/man/index.txt
+++ b/lib/bundler/man/index.txt
@@ -6,9 +6,11 @@ bundle-cache(1) bundle-cache.1
bundle-check(1) bundle-check.1
bundle-clean(1) bundle-clean.1
bundle-config(1) bundle-config.1
+bundle-console(1) bundle-console.1
bundle-doctor(1) bundle-doctor.1
bundle-exec(1) bundle-exec.1
bundle-gem(1) bundle-gem.1
+bundle-help(1) bundle-help.1
bundle-info(1) bundle-info.1
bundle-init(1) bundle-init.1
bundle-inject(1) bundle-inject.1
@@ -18,8 +20,10 @@ bundle-lock(1) bundle-lock.1
bundle-open(1) bundle-open.1
bundle-outdated(1) bundle-outdated.1
bundle-platform(1) bundle-platform.1
+bundle-plugin(1) bundle-plugin.1
bundle-pristine(1) bundle-pristine.1
bundle-remove(1) bundle-remove.1
bundle-show(1) bundle-show.1
bundle-update(1) bundle-update.1
+bundle-version(1) bundle-version.1
bundle-viz(1) bundle-viz.1
diff --git a/lib/bundler/match_metadata.rb b/lib/bundler/match_metadata.rb
new file mode 100644
index 0000000000..499036ca93
--- /dev/null
+++ b/lib/bundler/match_metadata.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Bundler
+ module MatchMetadata
+ def matches_current_ruby?
+ @required_ruby_version.satisfied_by?(Gem.ruby_version)
+ end
+
+ def matches_current_rubygems?
+ @required_rubygems_version.satisfied_by?(Gem.rubygems_version)
+ end
+ end
+end
diff --git a/lib/bundler/match_remote_metadata.rb b/lib/bundler/match_remote_metadata.rb
new file mode 100644
index 0000000000..5e46d52441
--- /dev/null
+++ b/lib/bundler/match_remote_metadata.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Bundler
+ module FetchMetadata
+ # A fallback is included because the original version of the specification
+ # API didn't include that field, so some marshalled specs in the index have it
+ # set to +nil+.
+ def matches_current_ruby?
+ @required_ruby_version ||= _remote_specification.required_ruby_version || Gem::Requirement.default
+
+ super
+ end
+
+ def matches_current_rubygems?
+ # A fallback is included because the original version of the specification
+ # API didn't include that field, so some marshalled specs in the index have it
+ # set to +nil+.
+ @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
+
+ super
+ end
+ end
+
+ module MatchRemoteMetadata
+ include MatchMetadata
+
+ prepend FetchMetadata
+ end
+end
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
index a63b45b47d..9d437a0951 100644
--- a/lib/bundler/mirror.rb
+++ b/lib/bundler/mirror.rb
@@ -148,13 +148,11 @@ module Bundler
class TCPSocketProbe
def replies?(mirror)
MirrorSockets.new(mirror).any? do |socket, address, timeout|
- begin
- socket.connect_nonblock(address)
- rescue Errno::EINPROGRESS
- wait_for_writtable_socket(socket, address, timeout)
- rescue RuntimeError # Connection failed somehow, again
- false
- end
+ socket.connect_nonblock(address)
+ rescue Errno::EINPROGRESS
+ wait_for_writtable_socket(socket, address, timeout)
+ rescue RuntimeError # Connection failed somehow, again
+ false
end
end
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
index 158c69e1a1..f3caff8963 100644
--- a/lib/bundler/plugin.rb
+++ b/lib/bundler/plugin.rb
@@ -15,7 +15,7 @@ module Bundler
class UnknownSourceError < PluginError; end
class PluginInstallError < PluginError; end
- PLUGIN_FILE_NAME = "plugins.rb".freeze
+ PLUGIN_FILE_NAME = "plugins.rb"
module_function
@@ -36,6 +36,8 @@ module Bundler
# @param [Hash] options various parameters as described in description.
# Refer to cli/plugin for available options
def install(names, options)
+ raise InvalidOption, "You cannot specify `--branch` and `--ref` at the same time." if options["branch"] && options["ref"]
+
specs = Installer.new.install(names, options)
save_plugins names, specs
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
index 29d33be718..a2d5eaa38a 100644
--- a/lib/bundler/plugin/index.rb
+++ b/lib/bundler/plugin/index.rb
@@ -146,7 +146,7 @@ module Bundler
# @param [Boolean] is the index file global index
def load_index(index_file, global = false)
SharedHelpers.filesystem_access(index_file, :read) do |index_f|
- valid_file = index_f && index_f.exist? && !index_f.size.zero?
+ valid_file = index_f&.exist? && !index_f.size.zero?
break unless valid_file
data = index_f.read
@@ -167,11 +167,11 @@ module Bundler
# to be only String key value pairs)
def save_index
index = {
- "commands" => @commands,
- "hooks" => @hooks,
- "load_paths" => @load_paths,
+ "commands" => @commands,
+ "hooks" => @hooks,
+ "load_paths" => @load_paths,
"plugin_paths" => @plugin_paths,
- "sources" => @sources,
+ "sources" => @sources,
}
require_relative "../yaml_serializer"
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
index 81ecafa470..c9ff12ce4b 100644
--- a/lib/bundler/plugin/installer.rb
+++ b/lib/bundler/plugin/installer.rb
@@ -83,8 +83,11 @@ module Bundler
Bundler.configure_gem_home_and_path(Plugin.root)
- definition = Definition.new(nil, deps, source_list, true)
- install_definition(definition)
+ Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ definition = Definition.new(nil, deps, source_list, true)
+
+ install_definition(definition)
+ end
end
# Installs the plugins and deps from the provided specs and returns map of
diff --git a/lib/bundler/plugin/installer/rubygems.rb b/lib/bundler/plugin/installer/rubygems.rb
index 7277234d9a..cb5db9c30e 100644
--- a/lib/bundler/plugin/installer/rubygems.rb
+++ b/lib/bundler/plugin/installer/rubygems.rb
@@ -6,10 +6,6 @@ module Bundler
class Rubygems < Bundler::Source::Rubygems
private
- def requires_sudo?
- false # Will change on implementation of project level plugins
- end
-
def rubygems_dir
Plugin.root
end
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index b5d7e3a6c9..f626a3218e 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -6,6 +6,7 @@ module Bundler
# be seeded with what we're given from the source's abbreviated index - the
# full specification will only be fetched when necessary.
class RemoteSpecification
+ include MatchRemoteMetadata
include MatchPlatform
include Comparable
@@ -28,18 +29,11 @@ module Bundler
@platform = _remote_specification.platform
end
- # A fallback is included because the original version of the specification
- # API didn't include that field, so some marshalled specs in the index have it
- # set to +nil+.
- def required_rubygems_version
- @required_rubygems_version ||= _remote_specification.required_rubygems_version || Gem::Requirement.default
- end
-
def full_name
- if @original_platform == Gem::Platform::RUBY
+ @full_name ||= if @platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
- "#{@name}-#{@version}-#{@original_platform}"
+ "#{@name}-#{@version}-#{@platform}"
end
end
@@ -108,7 +102,7 @@ module Bundler
def _remote_specification
@_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @original_platform])
@_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \
- " missing from the server! Try installing with `--full-index` as a workaround.")
+ " missing from the server!")
end
def method_missing(method, *args, &blk)
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index ca1bdbda7b..2ad35bc931 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -1,420 +1,427 @@
# frozen_string_literal: true
module Bundler
+ #
+ # This class implements the interface needed by PubGrub for resolution. It is
+ # equivalent to the `PubGrub::BasicPackageSource` class provided by PubGrub by
+ # default and used by the most simple PubGrub consumers.
+ #
class Resolver
- require_relative "vendored_molinillo"
- require_relative "resolver/spec_group"
+ require_relative "vendored_pub_grub"
+ require_relative "resolver/base"
+ require_relative "resolver/candidate"
+ require_relative "resolver/incompatibility"
+ require_relative "resolver/root"
include GemHelpers
- # Figures out the best possible configuration of gems that satisfies
- # the list of passed dependencies and any child dependencies without
- # causing any gem activation errors.
- #
- # ==== Parameters
- # *dependencies<Gem::Dependency>:: The list of dependencies to resolve
- #
- # ==== Returns
- # <GemBundle>,nil:: If the list of dependencies can be resolved, a
- # collection of gemspecs is returned. Otherwise, nil is returned.
- def self.resolve(requirements, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
- base = SpecSet.new(base) unless base.is_a?(SpecSet)
- metadata_requirements, regular_requirements = requirements.partition {|dep| dep.name.end_with?("\0") }
- resolver = new(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms, metadata_requirements)
- result = resolver.start(requirements)
- SpecSet.new(SpecSet.new(result).for(regular_requirements, false, platforms))
- end
-
- def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms, metadata_requirements)
- @source_requirements = source_requirements
- @metadata_requirements = metadata_requirements
+ def initialize(base, gem_version_promoter)
+ @source_requirements = base.source_requirements
@base = base
- @resolver = Molinillo::Resolver.new(self, self)
- @search_for = {}
- @base_dg = Molinillo::DependencyGraph.new
- base.each do |ls|
- dep = Dependency.new(ls.name, ls.version)
- @base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true)
- end
- additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
- @platforms = platforms.reject {|p| p != Gem::Platform::RUBY && (platforms - [p]).any? {|pl| generic(pl) == p } }
- @resolving_only_for_ruby = platforms == [Gem::Platform::RUBY]
@gem_version_promoter = gem_version_promoter
- @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major?
end
- def start(requirements)
- @gem_version_promoter.prerelease_specified = @prerelease_specified = {}
- requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? }
-
- verify_gemfile_dependencies_are_found!(requirements)
- dg = @resolver.resolve(requirements, @base_dg)
- dg.
- map(&:payload).
- reject {|sg| sg.name.end_with?("\0") }.
- map(&:to_specs).
- flatten
- rescue Molinillo::VersionConflict => e
- message = version_conflict_message(e)
- raise VersionConflict.new(e.conflicts.keys.uniq, message)
- rescue Molinillo::CircularDependencyError => e
- names = e.dependencies.sort_by(&:name).map {|d| "gem '#{d.name}'" }
- raise CyclicDependencyError, "Your bundle requires gems that depend" \
- " on each other, creating an infinite loop. Please remove" \
- " #{names.count > 1 ? "either " : ""}#{names.join(" or ")}" \
- " and try again."
- end
+ def start
+ @requirements = @base.requirements
+ @packages = @base.packages
- include Molinillo::UI
-
- # Conveys debug information to the user.
- #
- # @param [Integer] depth the current depth of the resolution process.
- # @return [void]
- def debug(depth = 0)
- return unless debug?
- debug_info = yield
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
- puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" }
- end
+ root, logger = setup_solver
- def debug?
- return @debug_mode if defined?(@debug_mode)
- @debug_mode =
- ENV["BUNDLER_DEBUG_RESOLVER"] ||
- ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
- ENV["DEBUG_RESOLVER"] ||
- ENV["DEBUG_RESOLVER_TREE"] ||
- false
- end
+ Bundler.ui.info "Resolving dependencies...", true
- def before_resolution
- Bundler.ui.info "Resolving dependencies...", debug?
+ solve_versions(:root => root, :logger => logger)
end
- def after_resolution
- Bundler.ui.info ""
- end
+ def setup_solver
+ root = Resolver::Root.new(name_for_explicit_dependency_source)
+ root_version = Resolver::Candidate.new(0)
- def indicate_progress
- Bundler.ui.info ".", false unless debug?
- end
+ @all_specs = Hash.new do |specs, name|
+ specs[name] = source_for(name).specs.search(name).reject do |s|
+ s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly
+ end.sort_by {|s| [s.version, s.platform.to_s] }
+ end
+
+ @sorted_versions = Hash.new do |candidates, package|
+ candidates[package] = if package.root?
+ [root_version]
+ else
+ all_versions_for(package).sort
+ end
+ end
- include Molinillo::SpecificationProvider
+ root_dependencies = prepare_dependencies(@requirements, @packages)
- def dependencies_for(specification)
- specification.dependencies_for_activated_platforms
+ @cached_dependencies = Hash.new do |dependencies, package|
+ dependencies[package] = if package.root?
+ { root_version => root_dependencies }
+ else
+ Hash.new do |versions, version|
+ versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
+ end
+ end
+ end
+
+ logger = Bundler::UI::Shell.new
+ logger.level = debug? ? "debug" : "warn"
+
+ [root, logger]
end
- def search_for(dependency_proxy)
- platform = dependency_proxy.__platform
- dependency = dependency_proxy.dep
- name = dependency.name
- @search_for[dependency_proxy] ||= begin
- results = results_for(dependency) + @base[name].select {|spec| requirement_satisfied_by?(dependency, nil, spec) }
+ def solve_versions(root:, logger:)
+ solver = PubGrub::VersionSolver.new(:source => self, :root => root, :logger => logger)
+ result = solver.solve
+ result.map {|package, version| version.to_specs(package) }.flatten.uniq
+ rescue PubGrub::SolveFailure => e
+ incompatibility = e.incompatibility
+
+ names_to_unlock, names_to_allow_prereleases_for, extended_explanation = find_names_to_relax(incompatibility)
+
+ names_to_relax = names_to_unlock + names_to_allow_prereleases_for
+
+ if names_to_relax.any?
+ if names_to_unlock.any?
+ Bundler.ui.debug "Found conflicts with locked dependencies. Will retry with #{names_to_unlock.join(", ")} unlocked...", true
- if vertex = @base_dg.vertex_named(name)
- locked_requirement = vertex.payload.requirement
+ @base.unlock_names(names_to_unlock)
end
- if !@prerelease_specified[name] && (!@use_gvp || locked_requirement.nil?)
- # Move prereleases to the beginning of the list, so they're considered
- # last during resolution.
- pre, results = results.partition {|spec| spec.version.prerelease? }
- results = pre + results
+ if names_to_allow_prereleases_for.any?
+ Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retrying considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true
+
+ @base.include_prereleases(names_to_allow_prereleases_for)
end
- spec_groups = if results.any?
- nested = []
- results.each do |spec|
- version, specs = nested.last
- if version == spec.version
- specs << spec
- else
- nested << [spec.version, [spec]]
- end
- end
- nested.reduce([]) do |groups, (version, specs)|
- next groups if locked_requirement && !locked_requirement.satisfied_by?(version)
- next groups unless specs.any? {|spec| spec.match_platform(platform) }
+ root, logger = setup_solver
- specs_by_platform = Hash.new do |current_specs, current_platform|
- current_specs[current_platform] = select_best_platform_match(specs, current_platform)
- end
+ Bundler.ui.debug "Retrying resolution...", true
+ retry
+ end
- spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY], Gem::Platform::RUBY)
- if spec_group_ruby
- spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform
- groups << spec_group_ruby
- end
+ explanation = e.message
- next groups if @resolving_only_for_ruby || dependency.force_ruby_platform
+ if extended_explanation
+ explanation << "\n\n"
+ explanation << extended_explanation
+ end
- spec_group = SpecGroup.create_for(specs_by_platform, @platforms, platform)
- groups << spec_group
+ raise SolveFailure.new(explanation)
+ end
- groups
+ def find_names_to_relax(incompatibility)
+ names_to_unlock = []
+ names_to_allow_prereleases_for = []
+ extended_explanation = nil
+
+ while incompatibility.conflict?
+ cause = incompatibility.cause
+ incompatibility = cause.incompatibility
+
+ incompatibility.terms.each do |term|
+ package = term.package
+ name = package.name
+
+ if base_requirements[name]
+ names_to_unlock << name
+ elsif package.ignores_prereleases?
+ names_to_allow_prereleases_for << name
end
- else
- []
- end
- # GVP handles major itself, but it's still a bit risky to trust it with it
- # until we get it settled with new behavior. For 2.x it can take over all cases.
- if !@use_gvp
- spec_groups
- else
- @gem_version_promoter.sort_versions(dependency, spec_groups)
+
+ no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(PubGrub::Incompatibility::NoVersions) }
+ next unless no_versions_incompat
+
+ extended_explanation = no_versions_incompat.extended_explanation
end
end
- end
- def index_for(dependency)
- source_for(dependency.name).specs
+ [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, extended_explanation]
end
- def source_for(name)
- @source_requirements[name] || @source_requirements[:default]
- end
+ def parse_dependency(package, dependency)
+ range = if repository_for(package).is_a?(Source::Gemspec)
+ PubGrub::VersionRange.any
+ else
+ requirement_to_range(dependency)
+ end
- def results_for(dependency)
- index_for(dependency).search(dependency)
+ PubGrub::VersionConstraint.new(package, :range => range)
end
- def name_for(dependency)
- dependency.name
- end
+ def versions_for(package, range=VersionRange.any)
+ versions = range.select_versions(@sorted_versions[package])
- def name_for_explicit_dependency_source
- Bundler.default_gemfile.basename.to_s
- rescue StandardError
- "Gemfile"
+ sort_versions(package, versions)
end
- def name_for_locking_dependency_source
- Bundler.default_lockfile.basename.to_s
- rescue StandardError
- "Gemfile.lock"
- end
+ def no_versions_incompatibility_for(package, unsatisfied_term)
+ cause = PubGrub::Incompatibility::NoVersions.new(unsatisfied_term)
+ name = package.name
+ constraint = unsatisfied_term.constraint
+ constraint_string = constraint.constraint_string
+ requirements = constraint_string.split(" OR ").map {|req| Gem::Requirement.new(req.split(",")) }
- def requirement_satisfied_by?(requirement, activated, spec)
- requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
- end
+ if name == "bundler" && bundler_pinned_to_current_version?
+ custom_explanation = "the current Bundler version (#{Bundler::VERSION}) does not satisfy #{constraint}"
+ extended_explanation = bundler_not_found_message(requirements)
+ else
+ specs_matching_other_platforms = filter_matching_specs(@all_specs[name], requirements)
- def dependencies_equal?(dependencies, other_dependencies)
- dependencies.map(&:dep) == other_dependencies.map(&:dep)
- end
+ platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : ""
+ custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}"
- def sort_dependencies(dependencies, activated, conflicts)
- dependencies.sort_by do |dependency|
- name = name_for(dependency)
- vertex = activated.vertex_named(name)
- [
- @base_dg.vertex_named(name) ? 0 : 1,
- vertex.payload ? 0 : 1,
- vertex.root? ? 0 : 1,
- amount_constrained(dependency),
- conflicts[name] ? 0 : 1,
- vertex.payload ? 0 : search_for(dependency).count,
- self.class.platform_sort_key(dependency.__platform),
- ]
+ label = "#{name} (#{constraint_string})"
+ extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
end
+
+ Incompatibility.new([unsatisfied_term], :cause => cause, :custom_explanation => custom_explanation, :extended_explanation => extended_explanation)
end
- def self.platform_sort_key(platform)
- # Prefer specific platform to not specific platform
- return ["99-LAST", "", "", ""] if Gem::Platform::RUBY == platform
- ["00", *platform.to_a.map {|part| part || "" }]
+ def debug?
+ ENV["BUNDLER_DEBUG_RESOLVER"] ||
+ ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
+ ENV["DEBUG_RESOLVER"] ||
+ ENV["DEBUG_RESOLVER_TREE"] ||
+ false
end
- private
+ def incompatibilities_for(package, version)
+ package_deps = @cached_dependencies[package]
+ sorted_versions = @sorted_versions[package]
+ package_deps[version].map do |dep_package, dep_constraint|
+ low = high = sorted_versions.index(version)
- # returns an integer \in (-\infty, 0]
- # a number closer to 0 means the dependency is less constraining
- #
- # dependencies w/ 0 or 1 possibilities (ignoring version requirements)
- # are given very negative values, so they _always_ sort first,
- # before dependencies that are unconstrained
- def amount_constrained(dependency)
- @amount_constrained ||= {}
- @amount_constrained[dependency.name] ||= if (base = @base[dependency.name]) && !base.empty?
- dependency.requirement.satisfied_by?(base.first.version) ? 0 : 1
- else
- all = index_for(dependency).search(dependency.name).size
+ # find version low such that all >= low share the same dep
+ while low > 0 && package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint
+ low -= 1
+ end
+ low =
+ if low == 0
+ nil
+ else
+ sorted_versions[low]
+ end
- if all <= 1
- all - 1_000_000
- else
- search = search_for(dependency)
- search = @prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? }
- search - all
+ # find version high such that all < high share the same dep
+ while high < sorted_versions.length && package_deps[sorted_versions[high]][dep_package] == dep_constraint
+ high += 1
+ end
+ high =
+ if high == sorted_versions.length
+ nil
+ else
+ sorted_versions[high]
+ end
+
+ range = PubGrub::VersionRange.new(:min => low, :max => high, :include_min => true)
+
+ self_constraint = PubGrub::VersionConstraint.new(package, :range => range)
+
+ dep_term = PubGrub::Term.new(dep_constraint, false)
+ self_term = PubGrub::Term.new(self_constraint, true)
+
+ custom_explanation = if dep_package.meta? && package.root?
+ "current #{dep_package} version is #{dep_constraint.constraint_string}"
end
+
+ PubGrub::Incompatibility.new([self_term, dep_term], :cause => :dependency, :custom_explanation => custom_explanation)
end
end
- def verify_gemfile_dependencies_are_found!(requirements)
- requirements.map! do |requirement|
- name = requirement.name
- next requirement if name == "bundler"
- next requirement unless search_for(requirement).empty?
- next unless requirement.current_platform?
-
- if (base = @base[name]) && !base.empty?
- version = base.first.version
- message = "You have requested:\n" \
- " #{name} #{requirement.requirement}\n\n" \
- "The bundle currently has #{name} locked at #{version}.\n" \
- "Try running `bundle update #{name}`\n\n" \
- "If you are updating multiple gems in your Gemfile at once,\n" \
- "try passing them all to `bundle update`"
- else
- message = gem_not_found_message(name, requirement, source_for(name))
- end
- raise GemNotFound, message
- end.compact!
+ def all_versions_for(package)
+ name = package.name
+ results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
+
+ if name == "bundler" && !bundler_pinned_to_current_version?
+ bundler_spec = Gem.loaded_specs["bundler"]
+ results << bundler_spec if bundler_spec
+ end
+
+ locked_requirement = base_requirements[name]
+ results = filter_matching_specs(results, locked_requirement) if locked_requirement
+
+ versions = results.group_by(&:version).reduce([]) do |groups, (version, specs)|
+ platform_specs = package.platforms.flat_map {|platform| select_best_platform_match(specs, platform) }
+ next groups if platform_specs.empty?
+
+ ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
+ groups << Resolver::Candidate.new(version, :specs => ruby_specs) if ruby_specs.any?
+
+ next groups if platform_specs == ruby_specs || package.force_ruby_platform?
+
+ groups << Resolver::Candidate.new(version, :specs => platform_specs)
+
+ groups
+ end
+
+ sort_versions(package, versions)
+ end
+
+ def source_for(name)
+ @source_requirements[name] || @source_requirements[:default]
+ end
+
+ def default_bundler_source
+ @source_requirements[:default_bundler]
+ end
+
+ def bundler_pinned_to_current_version?
+ !default_bundler_source.nil?
+ end
+
+ def name_for_explicit_dependency_source
+ Bundler.default_gemfile.basename.to_s
+ rescue StandardError
+ "Gemfile"
end
- def gem_not_found_message(name, requirement, source, extra_message = "")
- specs = source.specs.search(name)
+ def raise_not_found!(package)
+ name = package.name
+ source = source_for(name)
+ specs = @all_specs[name]
matching_part = name
- requirement_label = SharedHelpers.pretty_dependency(requirement)
+ requirement_label = SharedHelpers.pretty_dependency(package.dependency)
cache_message = begin
" or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
rescue GemfileNotFound
nil
end
- specs_matching_requirement = specs.select {| spec| requirement.matches_spec?(spec) }
+ specs_matching_requirement = filter_matching_specs(specs, package.dependency.requirement)
if specs_matching_requirement.any?
specs = specs_matching_requirement
matching_part = requirement_label
- requirement_label = "#{requirement_label}' with platform '#{requirement.__platform}"
+ platforms = package.platforms
+ platform_label = platforms.size == 1 ? "platform '#{platforms.first}" : "platforms '#{platforms.join("', '")}"
+ requirement_label = "#{requirement_label}' with #{platform_label}"
end
- message = String.new("Could not find gem '#{requirement_label}'#{extra_message} in #{source}#{cache_message}.\n")
+ message = String.new("Could not find gem '#{requirement_label}' in #{source}#{cache_message}.\n")
if specs.any?
- message << "\nThe source contains the following gems matching '#{matching_part}':\n"
- message << specs.map {|s| " * #{s.full_name}" }.join("\n")
+ message << "\n#{other_specs_matching_message(specs, matching_part)}"
end
- message
+ raise GemNotFound, message
end
- def version_conflict_message(e)
- # only show essential conflicts, if possible
- conflicts = e.conflicts.dup
+ private
+
+ def filter_matching_specs(specs, requirements)
+ Array(requirements).flat_map do |requirement|
+ specs.select {| spec| requirement_satisfied_by?(requirement, spec) }
+ end
+ end
- if conflicts["bundler"]
- conflicts.replace("bundler" => conflicts["bundler"])
+ def filter_prereleases(specs, package)
+ return specs unless package.ignores_prereleases? && specs.size > 1
+
+ specs.reject {|s| s.version.prerelease? }
+ end
+
+ def requirement_satisfied_by?(requirement, spec)
+ requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
+ end
+
+ def sort_versions(package, versions)
+ if versions.size > 1
+ @gem_version_promoter.sort_versions(package, versions).reverse
else
- conflicts.delete_if do |_name, conflict|
- deps = conflict.requirement_trees.map(&:last).flatten(1)
- !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
- end
+ versions
end
+ end
- e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
+ def repository_for(package)
+ source_for(package.name)
+ end
- e.message_with_trees(
- :full_message_for_conflict => lambda do |name, conflict|
- o = if name.end_with?("\0")
- String.new("Bundler found conflicting requirements for the #{name} version:")
- else
- String.new("Bundler could not find compatible versions for gem \"#{name}\":")
- end
- o << %(\n)
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{SharedHelpers.pretty_dependency(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = conflict.requirement_trees
+ def base_requirements
+ @base.base_requirements
+ end
- # called first, because we want to reduce the amount of work required to find maximal empty sets
- trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } }
+ def prepare_dependencies(requirements, packages)
+ to_dependency_hash(requirements, packages).map do |dep_package, dep_constraint|
+ name = dep_package.name
- # bail out if tree size is too big for Array#combination to make any sense
- if trees.size <= 15
- maximal = 1.upto(trees.size).map do |size|
- trees.map(&:last).flatten(1).combination(size).to_a
- end.flatten(1).select do |deps|
- Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
- end.min_by(&:size)
+ next [dep_package, dep_constraint] if name == "bundler"
- trees.reject! {|t| !maximal.include?(t.last) } if maximal
+ versions = versions_for(dep_package, dep_constraint.range)
+ if versions.empty? && dep_package.ignores_prereleases?
+ @sorted_versions.delete(dep_package)
+ dep_package.consider_prereleases!
+ versions = versions_for(dep_package, dep_constraint.range)
+ end
+ next [dep_package, dep_constraint] unless versions.empty?
- trees.sort_by! {|t| t.reverse.map(&:name) }
- end
+ next unless dep_package.current_platform?
- o << trees.map do |tree|
- t = "".dup
- depth = 2
-
- base_tree = tree.first
- base_tree_name = base_tree.name
-
- if base_tree_name.end_with?("\0")
- t = nil
- else
- tree.each do |req|
- t << " " * depth << SharedHelpers.pretty_dependency(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[req.name]
- t << %( was resolved to #{spec.version}, which)
- end
- t << %( depends on)
- end
- t << %(\n)
- depth += 1
- end
- end
- t
- end.compact.join("\n")
-
- if name == "bundler"
- o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
-
- conflict_dependency = conflict.requirement
- conflict_requirement = conflict_dependency.requirement
- other_bundler_required = !conflict_requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION))
-
- if other_bundler_required
- o << "\n\n"
-
- candidate_specs = source_for(:default_bundler).specs.search(conflict_dependency)
- if candidate_specs.any?
- target_version = candidate_specs.last.version
- new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
- o << "Your bundle requires a different version of Bundler than the one you're running.\n"
- o << "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
- else
- o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
- end
- end
- elsif name.end_with?("\0")
- o << %(\n Current #{name} version:\n #{SharedHelpers.pretty_dependency(@metadata_requirements.find {|req| req.name == name })}\n\n)
- elsif conflict.locked_requirement
- o << "\n"
- o << %(Deleting your #{name_for_locking_dependency_source} file and running `bundle install` will rebuild your snapshot from scratch, using only\n)
- o << %(the gems in your Gemfile, which may resolve the conflict.\n)
- elsif !conflict.existing
- o << "\n"
-
- relevant_source = conflict.requirement.source || source_for(name)
-
- extra_message = if conflict.requirement_trees.first.size > 1
- ", which is required by gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}',"
- else
- ""
- end
-
- o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
- end
+ raise_not_found!(dep_package)
+ end.compact.to_h
+ end
- o
+ def other_specs_matching_message(specs, requirement)
+ message = String.new("The source contains the following gems matching '#{requirement}':\n")
+ message << specs.map {|s| " * #{s.full_name}" }.join("\n")
+ message
+ end
+
+ def requirement_to_range(requirement)
+ ranges = requirement.requirements.map do |(op, version)|
+ ver = Resolver::Candidate.new(version).generic!
+ platform_ver = Resolver::Candidate.new(version).platform_specific!
+
+ case op
+ when "~>"
+ name = "~> #{ver}"
+ bump = Resolver::Candidate.new(version.bump.to_s + ".A")
+ PubGrub::VersionRange.new(:name => name, :min => ver, :max => bump, :include_min => true)
+ when ">"
+ PubGrub::VersionRange.new(:min => platform_ver)
+ when ">="
+ PubGrub::VersionRange.new(:min => ver, :include_min => true)
+ when "<"
+ PubGrub::VersionRange.new(:max => ver)
+ when "<="
+ PubGrub::VersionRange.new(:max => platform_ver, :include_max => true)
+ when "="
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true)
+ when "!="
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true).invert
+ else
+ raise "bad version specifier: #{op}"
end
- )
+ end
+
+ ranges.inject(&:intersect)
+ end
+
+ def to_dependency_hash(dependencies, packages)
+ dependencies.inject({}) do |deps, dep|
+ package = packages[dep.name]
+
+ current_req = deps[package]
+ new_req = parse_dependency(package, dep.requirement)
+
+ deps[package] = if current_req
+ current_req.intersect(new_req)
+ else
+ new_req
+ end
+
+ deps
+ end
+ end
+
+ def bundler_not_found_message(conflict_dependencies)
+ candidate_specs = filter_matching_specs(default_bundler_source.specs.search("bundler"), conflict_dependencies)
+
+ if candidate_specs.any?
+ target_version = candidate_specs.last.version
+ new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
+ "Your bundle requires a different version of Bundler than the one you're running.\n" \
+ "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
+ else
+ "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
+ end
end
end
end
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb
new file mode 100644
index 0000000000..e5c3763c3f
--- /dev/null
+++ b/lib/bundler/resolver/base.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require_relative "package"
+
+module Bundler
+ class Resolver
+ class Base
+ attr_reader :packages, :requirements, :source_requirements
+
+ def initialize(source_requirements, dependencies, base, platforms, options)
+ @source_requirements = source_requirements
+
+ @base = base
+
+ @packages = Hash.new do |hash, name|
+ hash[name] = Package.new(name, platforms, **options)
+ end
+
+ @requirements = dependencies.map do |dep|
+ dep_platforms = dep.gem_platforms(platforms)
+
+ # Dependencies scoped to external platforms are ignored
+ next if dep_platforms.empty?
+
+ name = dep.name
+
+ @packages[name] = Package.new(name, dep_platforms, **options.merge(:dependency => dep))
+
+ dep
+ end.compact
+ end
+
+ def [](name)
+ @base[name]
+ end
+
+ def delete(specs)
+ @base.delete(specs)
+ end
+
+ def get_package(name)
+ @packages[name]
+ end
+
+ def base_requirements
+ @base_requirements ||= build_base_requirements
+ end
+
+ def unlock_names(names)
+ indirect_pins = indirect_pins(names)
+
+ if indirect_pins.any?
+ loosen_names(indirect_pins)
+ else
+ pins = pins(names)
+
+ if pins.any?
+ loosen_names(pins)
+ else
+ unrestrict_names(names)
+ end
+ end
+ end
+
+ def include_prereleases(names)
+ names.each do |name|
+ get_package(name).consider_prereleases!
+ end
+ end
+
+ private
+
+ def indirect_pins(names)
+ names.select {|name| @base_requirements[name].exact? && @requirements.none? {|dep| dep.name == name } }
+ end
+
+ def pins(names)
+ names.select {|name| @base_requirements[name].exact? }
+ end
+
+ def loosen_names(names)
+ names.each do |name|
+ version = @base_requirements[name].requirements.first[1]
+
+ @base_requirements[name] = Gem::Requirement.new(">= #{version}")
+
+ @base.delete_by_name(name)
+ end
+ end
+
+ def unrestrict_names(names)
+ names.each do |name|
+ @base_requirements.delete(name)
+ end
+ end
+
+ def build_base_requirements
+ base_requirements = {}
+ @base.each do |ls|
+ req = Gem::Requirement.new(ls.version)
+ base_requirements[ls.name] = req
+ end
+ base_requirements
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/candidate.rb b/lib/bundler/resolver/candidate.rb
new file mode 100644
index 0000000000..e695ef08ee
--- /dev/null
+++ b/lib/bundler/resolver/candidate.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require_relative "spec_group"
+
+module Bundler
+ class Resolver
+ #
+ # This class is a PubGrub compatible "Version" class that takes Bundler
+ # resolution complexities into account.
+ #
+ # Each Resolver::Candidate has a underlying `Gem::Version` plus a set of
+ # platforms. For example, 1.1.0-x86_64-linux is a different resolution candidate
+ # from 1.1.0 (generic). This is because different platform variants of the
+ # same gem version can bring different dependencies, so they need to be
+ # considered separately.
+ #
+ # Some candidates may also keep some information explicitly about the
+ # package the refer to. These candidates are referred to as "canonical" and
+ # are used when materializing resolution results back into RubyGems
+ # specifications that can be installed, written to lock files, and so on.
+ #
+ class Candidate
+ include Comparable
+
+ attr_reader :version
+
+ def initialize(version, specs: [])
+ @spec_group = Resolver::SpecGroup.new(specs)
+ @version = Gem::Version.new(version)
+ @ruby_only = specs.map(&:platform).uniq == [Gem::Platform::RUBY]
+ end
+
+ def dependencies
+ @spec_group.dependencies
+ end
+
+ def to_specs(package)
+ return [] if package.meta?
+
+ @spec_group.to_specs(package.force_ruby_platform?)
+ end
+
+ def generic!
+ @ruby_only = true
+
+ self
+ end
+
+ def platform_specific!
+ @ruby_only = false
+
+ self
+ end
+
+ def prerelease?
+ @version.prerelease?
+ end
+
+ def segments
+ @version.segments
+ end
+
+ def sort_obj
+ [@version, @ruby_only ? -1 : 1]
+ end
+
+ def <=>(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj <=> other.sort_obj
+ end
+
+ def ==(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj == other.sort_obj
+ end
+
+ def eql?(other)
+ return unless other.is_a?(self.class)
+
+ sort_obj.eql?(other.sort_obj)
+ end
+
+ def hash
+ sort_obj.hash
+ end
+
+ def to_s
+ @version.to_s
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/incompatibility.rb b/lib/bundler/resolver/incompatibility.rb
new file mode 100644
index 0000000000..c61151fbeb
--- /dev/null
+++ b/lib/bundler/resolver/incompatibility.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ class Incompatibility < PubGrub::Incompatibility
+ attr_reader :extended_explanation
+
+ def initialize(terms, cause:, custom_explanation: nil, extended_explanation: nil)
+ @extended_explanation = extended_explanation
+
+ super(terms, :cause => cause, :custom_explanation => custom_explanation)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb
new file mode 100644
index 0000000000..7499a75006
--- /dev/null
+++ b/lib/bundler/resolver/package.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ #
+ # Represents a gem being resolved, in a format PubGrub likes.
+ #
+ # The class holds the following information:
+ #
+ # * Platforms this gem will be resolved on.
+ # * The locked version of this gem resolution should favor (if any).
+ # * Whether the gem should be unlocked to its latest version.
+ # * The dependency explicit set in the Gemfile for this gem (if any).
+ #
+ class Package
+ attr_reader :name, :platforms, :dependency, :locked_version
+
+ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, dependency: nil)
+ @name = name
+ @platforms = platforms
+ @locked_version = locked_specs[name].first&.version
+ @unlock = unlock
+ @dependency = dependency || Dependency.new(name, @locked_version)
+ @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
+ end
+
+ def to_s
+ @name.delete("\0")
+ end
+
+ def root?
+ false
+ end
+
+ def meta?
+ @name.end_with?("\0")
+ end
+
+ def ==(other)
+ self.class == other.class && @name == other.name
+ end
+
+ def hash
+ @name.hash
+ end
+
+ def unlock?
+ @unlock.empty? || @unlock.include?(name)
+ end
+
+ def ignores_prereleases?
+ @prerelease == :ignore
+ end
+
+ def prerelease_specified?
+ @prerelease == :consider_first
+ end
+
+ def consider_prereleases!
+ @prerelease = :consider_last
+ end
+
+ def force_ruby_platform?
+ @dependency.force_ruby_platform
+ end
+
+ def current_platform?
+ @dependency.current_platform?
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/root.rb b/lib/bundler/resolver/root.rb
new file mode 100644
index 0000000000..e5eb634fb8
--- /dev/null
+++ b/lib/bundler/resolver/root.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "package"
+
+module Bundler
+ class Resolver
+ #
+ # Represents the Gemfile from the resolver's perspective. It's the root
+ # package and Gemfile entries depend on it.
+ #
+ class Root < Package
+ def initialize(name)
+ @name = name
+ end
+
+ def meta?
+ true
+ end
+
+ def root?
+ true
+ end
+ end
+ end
+end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
index 4de5b91aa6..b44c19a73f 100644
--- a/lib/bundler/resolver/spec_group.rb
+++ b/lib/bundler/resolver/spec_group.rb
@@ -3,108 +3,79 @@
module Bundler
class Resolver
class SpecGroup
- attr_accessor :name, :version, :source
- attr_accessor :activated_platforms, :force_ruby_platform
-
- def self.create_for(specs, all_platforms, specific_platform)
- specific_platform_specs = specs[specific_platform]
- return unless specific_platform_specs.any?
-
- platforms = all_platforms.select {|p| specs[p].any? }
-
- new(specific_platform_specs.first, specs, platforms)
+ def initialize(specs)
+ @specs = specs
end
- def initialize(exemplary_spec, specs, relevant_platforms)
- @exemplary_spec = exemplary_spec
- @name = exemplary_spec.name
- @version = exemplary_spec.version
- @source = exemplary_spec.source
-
- @activated_platforms = relevant_platforms
- @dependencies = Hash.new do |dependencies, platforms|
- dependencies[platforms] = dependencies_for(platforms)
- end
- @specs = specs
+ def empty?
+ @specs.empty?
end
- def to_specs
- activated_platforms.map do |p|
- specs = @specs[p]
- next unless specs.any?
-
- specs.map do |s|
- lazy_spec = LazySpecification.new(name, version, s.platform, source)
- lazy_spec.force_ruby_platform = force_ruby_platform
- lazy_spec.dependencies.replace s.dependencies
- lazy_spec
- end
- end.flatten.compact.uniq
+ def name
+ @name ||= exemplary_spec.name
end
- def to_s
- activated_platforms_string = sorted_activated_platforms.join(", ")
- "#{name} (#{version}) (#{activated_platforms_string})"
+ def version
+ @version ||= exemplary_spec.version
end
- def dependencies_for_activated_platforms
- @dependencies[activated_platforms]
+ def source
+ @source ||= exemplary_spec.source
end
- def ==(other)
- return unless other.is_a?(SpecGroup)
- name == other.name &&
- version == other.version &&
- sorted_activated_platforms == other.sorted_activated_platforms &&
- source == other.source
+ def to_specs(force_ruby_platform)
+ @specs.map do |s|
+ lazy_spec = LazySpecification.new(name, version, s.platform, source)
+ lazy_spec.force_ruby_platform = force_ruby_platform
+ lazy_spec.dependencies.replace s.dependencies
+ lazy_spec
+ end
end
- def eql?(other)
- return unless other.is_a?(SpecGroup)
- name.eql?(other.name) &&
- version.eql?(other.version) &&
- sorted_activated_platforms.eql?(other.sorted_activated_platforms) &&
- source.eql?(other.source)
+ def to_s
+ sorted_spec_names.join(", ")
end
- def hash
- name.hash ^ version.hash ^ sorted_activated_platforms.hash ^ source.hash
+ def dependencies
+ @dependencies ||= @specs.map do |spec|
+ __dependencies(spec) + metadata_dependencies(spec)
+ end.flatten.uniq
end
protected
- def sorted_activated_platforms
- activated_platforms.sort_by(&:to_s)
+ def sorted_spec_names
+ @sorted_spec_names ||= @specs.map(&:full_name).sort
end
private
- def dependencies_for(platforms)
- platforms.map do |platform|
- __dependencies(platform) + metadata_dependencies(platform)
- end.flatten
+ def exemplary_spec
+ @specs.first
end
- def __dependencies(platform)
+ def __dependencies(spec)
dependencies = []
- @specs[platform].first.dependencies.each do |dep|
+ spec.dependencies.each do |dep|
next if dep.type == :development
- dependencies << DepProxy.get_proxy(Dependency.new(dep.name, dep.requirement), platform)
+ dependencies << Dependency.new(dep.name, dep.requirement)
end
dependencies
end
- def metadata_dependencies(platform)
- spec = @specs[platform].first
+ def metadata_dependencies(spec)
return [] if spec.is_a?(LazySpecification)
- dependencies = []
- unless spec.required_ruby_version.none?
- dependencies << DepProxy.get_proxy(Dependency.new("Ruby\0", spec.required_ruby_version), platform)
- end
- unless spec.required_rubygems_version.none?
- dependencies << DepProxy.get_proxy(Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
- end
- dependencies
+
+ [
+ metadata_dependency("Ruby", spec.required_ruby_version),
+ metadata_dependency("RubyGems", spec.required_rubygems_version),
+ ].compact
+ end
+
+ def metadata_dependency(name, requirement)
+ return if requirement.nil? || requirement.none?
+
+ Dependency.new("#{name}\0", requirement)
end
end
end
diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb
index 3b3a0583a5..d054969e8d 100644
--- a/lib/bundler/ruby_dsl.rb
+++ b/lib/bundler/ruby_dsl.rb
@@ -5,9 +5,15 @@ module Bundler
def ruby(*ruby_version)
options = ruby_version.last.is_a?(Hash) ? ruby_version.pop : {}
ruby_version.flatten!
+
raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
+ if options[:file]
+ raise GemfileError, "Cannot specify version when using the file option" if ruby_version.any?
+ ruby_version << Bundler.read_file(options[:file]).strip
+ end
+
if options[:engine] == "ruby" && options[:engine_version] &&
ruby_version != Array(options[:engine_version])
raise GemfileEvalError, "ruby_version must match the :engine_version for MRI"
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index 9161c6afde..b5396abb6e 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -28,8 +28,8 @@ module Bundler
end
@gem_version = Gem::Requirement.create(@versions.first).requirements.first.last
- @input_engine = engine && engine.to_s
- @engine = engine && engine.to_s || "ruby"
+ @input_engine = engine&.to_s
+ @engine = engine&.to_s || "ruby"
@engine_versions = (engine_version && Array(engine_version)) || @versions
@engine_gem_version = Gem::Requirement.create(@engine_versions.first).requirements.first.last
@patchlevel = patchlevel || (@gem_version.prerelease? ? "-1" : nil)
@@ -107,7 +107,7 @@ module Bundler
ruby_engine_version = RUBY_ENGINE == "ruby" ? ruby_version : RUBY_ENGINE_VERSION.dup
patchlevel = RUBY_PATCHLEVEL.to_s
- @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
+ @system ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
end
private
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index 938c58e64d..8981612706 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -15,6 +15,8 @@ require "rubygems/specification"
# `Gem::Source` from the redefined `Gem::Specification#source`.
require "rubygems/source"
+require_relative "match_metadata"
+require_relative "force_platform"
require_relative "match_platform"
# Cherry-pick fixes to `Gem.ruby_version` to be useful for modern Bundler
@@ -28,6 +30,7 @@ end
module Gem
class Specification
+ include ::Bundler::MatchMetadata
include ::Bundler::MatchPlatform
attr_accessor :remote, :location, :relative_loaded_from
@@ -63,7 +66,9 @@ module Gem
alias_method :rg_extension_dir, :extension_dir
def extension_dir
- @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name)
+ # following instance variable is already used in original method
+ # and that is the reason to prefix it with bundler_ and add rubocop exception
+ @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name) # rubocop:disable Naming/MemoizedInstanceVariableName
unique_extension_dir = [source.extension_dir_name, File.basename(full_gem_path)].uniq.join("-")
File.expand_path(File.join(extensions_dir, unique_extension_dir))
else
@@ -151,12 +156,16 @@ module Gem
end
class Dependency
+ include ::Bundler::ForcePlatform
+
attr_accessor :source, :groups
alias_method :eql?, :==
def force_ruby_platform
- false
+ return @force_ruby_platform if defined?(@force_ruby_platform) && !@force_ruby_platform.nil?
+
+ @force_ruby_platform = default_force_ruby_platform
end
def encode_with(coder)
@@ -196,9 +205,9 @@ module Gem
protected
def _requirements_sorted?
- return @_are_requirements_sorted if defined?(@_are_requirements_sorted)
+ return @_requirements_sorted if defined?(@_requirements_sorted)
strings = as_list
- @_are_requirements_sorted = strings == strings.sort
+ @_requirements_sorted = strings == strings.sort
end
def _with_sorted_requirements
@@ -235,6 +244,50 @@ module Gem
MINGW = Gem::Platform.new("x86-mingw32")
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
Gem::Platform.new("x64-mingw-ucrt")].freeze
+ WINDOWS = [MSWIN, MSWIN64, MINGW, X64_MINGW].flatten.freeze
+ X64_LINUX = Gem::Platform.new("x86_64-linux")
+ X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
+
+ if X64_LINUX === X64_LINUX_MUSL
+ remove_method :===
+
+ def ===(other)
+ return nil unless Gem::Platform === other
+
+ # universal-mingw32 matches x64-mingw-ucrt
+ return true if (@cpu == "universal" || other.cpu == "universal") &&
+ @os.start_with?("mingw") && other.os.start_with?("mingw")
+
+ # cpu
+ ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
+ (@cpu == "arm" && other.cpu.start_with?("arm"))) &&
+
+ # os
+ @os == other.os &&
+
+ # version
+ (
+ (@os != "linux" && (@version.nil? || other.version.nil?)) ||
+ (@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
+ @version == other.version
+ )
+ end
+
+ # This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
+ # Once only 3.3.23 is supported, we can use the method in RubyGems.
+ def normalized_linux_version_ext
+ return nil unless @version
+
+ without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
+ return nil if without_gnu_nor_abi_modifiers.empty?
+
+ without_gnu_nor_abi_modifiers
+ end
+ end
+
+ if RUBY_ENGINE == "truffleruby" && !defined?(REUSE_AS_BINARY_ON_TRUFFLERUBY)
+ REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 libv8-node sorbet-static].freeze
+ end
end
Platform.singleton_class.module_eval do
@@ -246,14 +299,43 @@ module Gem
def match_gem?(platform, gem_name)
match_platforms?(platform, Gem.platforms)
end
+ end
+
+ match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
+
+ if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
private
+ remove_method :match_platforms? if match_platforms_defined
+
def match_platforms?(platform, platforms)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
- (local_platform != Gem::Platform::RUBY && local_platform =~ platform)
+ (local_platform != Gem::Platform::RUBY && platform =~ local_platform)
+ end
+ end
+ end
+ end
+
+ # On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
+ class Specification
+ if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
+ local_platform = Platform.local
+ if local_platform.cpu == "universal"
+ ORIGINAL_LOCAL_PLATFORM = local_platform.to_s.freeze
+
+ local_platform.cpu = if arch == "arm64e" # arm64e is only permitted for Apple system binaries
+ "arm64"
+ else
+ arch
+ end
+
+ def extensions_dir
+ Gem.default_ext_dir_for(base_dir) ||
+ File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM,
+ Gem.extension_api_version)
end
end
end
@@ -267,11 +349,7 @@ module Gem
end
def glob_files_in_dir(glob, base_path)
- if RUBY_VERSION >= "2.5"
- Dir.glob(glob, :base => base_path).map! {|f| File.expand_path(f, base_path) }
- else
- Dir.glob(File.join(base_path.to_s.gsub(/[\[\]]/, '\\\\\\&'), glob)).map! {|f| File.expand_path(f) }
- end
+ Dir.glob(glob, :base => base_path).map! {|f| File.expand_path(f, base_path) }
end
end
end
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index 13c2d25882..38035a00ac 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -109,8 +109,10 @@ module Bundler
def strict_rm_rf(dir)
Bundler.rm_rf dir
- rescue Errno::ENOTEMPTY => e
- raise DirectoryRemovalError.new(e.cause, "Could not delete previous installation of `#{dir}`")
+ rescue StandardError => e
+ raise unless File.exist?(dir)
+
+ raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
end
def validate_bundler_checksum(checksum)
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index 08af0610c6..d8b7886af7 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -227,10 +227,14 @@ module Bundler
def reverse_rubygems_kernel_mixin
# Disable rubygems' gem activation system
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
- if k.private_method_defined?(:gem_original_require)
- redefine_method(k, :require, k.instance_method(:gem_original_require))
+ if Gem.respond_to?(:discover_gems_on_require=)
+ Gem.discover_gems_on_require = false
+ else
+ kernel = (class << ::Kernel; self; end)
+ [kernel, ::Kernel].each do |k|
+ if k.private_method_defined?(:gem_original_require)
+ redefine_method(k, :require, k.instance_method(:gem_original_require))
+ end
end
end
end
@@ -243,7 +247,7 @@ module Bundler
kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |kernel_class|
redefine_method(kernel_class, :gem) do |dep, *reqs|
- if executables && executables.include?(File.basename(caller.first.split(":").first))
+ if executables&.include?(File.basename(caller.first.split(":").first))
break
end
@@ -449,7 +453,7 @@ module Bundler
fetcher = gem_remote_fetcher
fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri
string = fetcher.fetch_path(path)
- Bundler.load_marshal(string)
+ Bundler.safe_load_marshal(string)
rescue Gem::RemoteFetcher::FetchError
# it's okay for prerelease to fail
raise unless name == "prerelease_specs"
@@ -504,10 +508,6 @@ module Bundler
Gem::Package.build(spec, skip_validation)
end
- def repository_subdirectories
- Gem::REPOSITORY_SUBDIRECTORIES
- end
-
def path_separator
Gem.path_separator
end
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
index bd38353d3c..95cf78dd41 100644
--- a/lib/bundler/runtime.rb
+++ b/lib/bundler/runtime.rb
@@ -94,7 +94,7 @@ module Bundler
definition_method :requires
def lock(opts = {})
- return if @definition.nothing_changed? && !@definition.unlocking?
+ return if @definition.no_resolve_needed?
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
end
diff --git a/lib/bundler/safe_marshal.rb b/lib/bundler/safe_marshal.rb
new file mode 100644
index 0000000000..50aa0f60a6
--- /dev/null
+++ b/lib/bundler/safe_marshal.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Bundler
+ module SafeMarshal
+ ALLOWED_CLASSES = [
+ Array,
+ FalseClass,
+ Gem::Specification,
+ Gem::Version,
+ Hash,
+ String,
+ Symbol,
+ Time,
+ TrueClass,
+ ].freeze
+
+ ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed."
+
+ PROC = proc do |object|
+ object.tap do
+ unless ALLOWED_CLASSES.include?(object.class)
+ raise TypeError, format(ERROR, object.class, ALLOWED_CLASSES.join(", "))
+ end
+ end
+ end
+
+ def self.proc
+ PROC
+ end
+ end
+end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index cf5675274e..0af2236a45 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -43,9 +43,7 @@ module Bundler
setup_makes_kernel_gem_public
silence_deprecations
silence_root_warning
- suppress_install_using_messages
update_requires_all_flag
- use_gem_version_promoter_for_major_updates
].freeze
NUMBER_KEYS = %w[
@@ -220,7 +218,6 @@ module Bundler
def path
configs.each do |_level, settings|
path = value_for("path", settings)
- path = "vendor/bundle" if value_for("deployment", settings) && path.nil?
path_system = value_for("path.system", settings)
disabled_shared_gems = value_for("disable_shared_gems", settings)
next if path.nil? && path_system.nil? && disabled_shared_gems.nil?
@@ -228,7 +225,9 @@ module Bundler
return Path.new(path, system_path)
end
- Path.new(nil, false)
+ path = "vendor/bundle" if self[:deployment]
+
+ Path.new(path, false)
end
Path = Struct.new(:explicit_path, :system_path) do
@@ -278,12 +277,6 @@ module Bundler
end
end
- def allow_sudo?
- key = key_for(:path)
- path_configured = @temporary.key?(key) || @local_config.key?(key)
- !path_configured
- end
-
def ignore_config?
ENV["BUNDLE_IGNORE_CONFIG"]
end
@@ -502,7 +495,7 @@ module Bundler
uri = $2
suffix = $3
end
- uri = "#{uri}/" unless uri.end_with?("/")
+ uri = URINormalizer.normalize_suffix(uri)
require_relative "vendored_uri"
uri = Bundler::URI(uri)
unless uri.absolute?
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 32e9b2d7c0..801fd5312a 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -12,7 +12,10 @@ if Bundler::SharedHelpers.in_bundle?
Bundler.ui.error e.message
Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"]
if e.is_a?(Bundler::GemNotFound)
- Bundler.ui.warn "Run `bundle install` to install missing gems."
+ suggested_cmd = "bundle install"
+ original_gemfile = Bundler.original_env["BUNDLE_GEMFILE"]
+ suggested_cmd += " --gemfile #{original_gemfile}" if original_gemfile
+ Bundler.ui.warn "Run `#{suggested_cmd}` to install missing gems."
end
exit e.status_code
end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index 8c4e26f074..d1d4e1d07a 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -160,10 +160,10 @@ module Bundler
" (was expecting #{old_deps.map(&:to_s)}, but the real spec has #{new_deps.map(&:to_s)})"
raise APIResponseMismatchError,
"Downloading #{spec.full_name} revealed dependencies not in the API or the lockfile (#{extra_deps.join(", ")})." \
- "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem."
+ "\nRunning `bundle update #{spec.name}` should fix the problem."
end
- def pretty_dependency(dep, print_source = false)
+ def pretty_dependency(dep)
msg = String.new(dep.name)
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
@@ -172,7 +172,6 @@ module Bundler
msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY
end
- msg << " from the `#{dep.source}` source" if print_source && dep.source
msg
end
@@ -285,6 +284,7 @@ module Bundler
Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", exe_file
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile.to_s
Bundler::SharedHelpers.set_env "BUNDLER_VERSION", Bundler::VERSION
+ Bundler::SharedHelpers.set_env "BUNDLER_SETUP", File.expand_path("setup", __dir__) unless RUBY_VERSION < "2.7"
end
def set_path
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index 69804a2e63..f7f5ea7865 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -100,7 +100,7 @@ module Bundler
end
def print_using_message(message)
- if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages?
+ if !message.include?("(was ")
Bundler.ui.debug message
else
Bundler.ui.info message
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index ed66dcdc12..adbce5fce4 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -19,7 +19,7 @@ module Bundler
# Stringify options that could be set as symbols
%w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] }
- @uri = options["uri"] || ""
+ @uri = URINormalizer.normalize_suffix(options["uri"] || "", :trailing_slash => false)
@safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
@branch = options["branch"]
@ref = options["ref"] || options["branch"] || options["tag"]
@@ -46,6 +46,14 @@ module Bundler
out << " specs:\n"
end
+ def to_gemfile
+ specifiers = %w[ref branch tag submodules glob].map do |opt|
+ "#{opt}: #{options[opt]}" if options[opt]
+ end
+
+ uri_with_specifiers(specifiers)
+ end
+
def hash
[self.class, uri, ref, branch, name, version, glob, submodules].hash
end
@@ -59,28 +67,32 @@ module Bundler
alias_method :==, :eql?
+ def include?(other)
+ other.is_a?(Git) && uri == other.uri &&
+ name == other.name &&
+ glob == other.glob &&
+ submodules == other.submodules
+ end
+
def to_s
begin
- at = if local?
- path
- elsif user_ref = options["ref"]
- if ref =~ /\A[a-z0-9]{4,}\z/i
- shortref_for_display(user_ref)
- else
- user_ref
- end
- elsif ref
- ref
- else
- git_proxy.branch
- end
+ at = humanized_ref || current_branch
rev = "at #{at}@#{shortref_for_display(revision)}"
rescue GitError
""
end
- specifiers = [rev, glob_for_display].compact
+ uri_with_specifiers([rev, glob_for_display])
+ end
+
+ def identifier
+ uri_with_specifiers([humanized_ref, cached_revision, glob_for_display])
+ end
+
+ def uri_with_specifiers(specifiers)
+ specifiers.compact!
+
suffix =
if specifiers.any?
" (#{specifiers.join(", ")})"
@@ -102,13 +114,7 @@ module Bundler
@install_path ||= begin
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
- path = Bundler.install_path.join(git_scope)
-
- if !path.exist? && Bundler.requires_sudo?
- Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
- else
- path
- end
+ Bundler.install_path.join(git_scope)
end
end
@@ -132,7 +138,7 @@ module Bundler
path = Pathname.new(path)
path = path.expand_path(Bundler.root) unless path.relative?
- unless options["branch"] || Bundler.settings[:disable_local_branch_check]
+ unless branch || Bundler.settings[:disable_local_branch_check]
raise GitError, "Cannot use local override for #{name} at #{path} because " \
":branch is not specified in Gemfile. Specify a branch or run " \
"`bundle config unset local.#{override_for(original_path)}` to remove the local override"
@@ -147,14 +153,14 @@ module Bundler
# Create a new git proxy without the cached revision
# so the Gemfile.lock always picks up the new revision.
- @git_proxy = GitProxy.new(path, uri, ref)
+ @git_proxy = GitProxy.new(path, uri, options)
- if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
+ if current_branch != branch && !Bundler.settings[:disable_local_branch_check]
raise GitError, "Local override for #{name} at #{path} is using branch " \
- "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
+ "#{current_branch} but Gemfile specifies #{branch}"
end
- changed = cached_revision && cached_revision != git_proxy.revision
+ changed = cached_revision && cached_revision != revision
if !Bundler.settings[:disable_local_revision_check] && changed && !@unlocked && !git_proxy.contains?(cached_revision)
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
@@ -179,6 +185,7 @@ module Bundler
end
def install(spec, options = {})
+ return if Bundler.settings[:no_install]
force = options[:force]
print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}"
@@ -219,7 +226,7 @@ module Bundler
# across different projects, this cache will be shared.
# When using local git repos, this is set to the local repo.
def cache_path
- @cache_path ||= if Bundler.requires_sudo? || Bundler.feature_flag.global_gem_cache?
+ @cache_path ||= if Bundler.feature_flag.global_gem_cache?
Bundler.user_cache
else
Bundler.bundle_path.join("cache", "bundler")
@@ -234,6 +241,10 @@ module Bundler
git_proxy.revision
end
+ def current_branch
+ git_proxy.current_branch
+ end
+
def allow_git_ops?
@allow_remote || @allow_cached
end
@@ -244,6 +255,20 @@ module Bundler
private
+ def humanized_ref
+ if local?
+ path
+ elsif user_ref = options["ref"]
+ if /\A[a-z0-9]{4,}\z/i.match?(ref)
+ shortref_for_display(user_ref)
+ else
+ user_ref
+ end
+ elsif ref
+ ref
+ end
+ end
+
def serialize_gemspecs_in(destination)
destination = destination.expand_path(Bundler.root) if destination.relative?
Dir["#{destination}/#{@glob}"].each do |spec_path|
@@ -297,7 +322,7 @@ module Bundler
end
def uri_hash
- if uri =~ %r{^\w+://(\w+@)?}
+ if %r{^\w+://(\w+@)?}.match?(uri)
# Downcase the domain component of the URI
# and strip off a trailing slash, if one is present
input = Bundler::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
@@ -319,7 +344,7 @@ module Bundler
end
def git_proxy
- @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
+ @git_proxy ||= GitProxy.new(cache_path, uri, options, cached_revision, self)
end
def fetch
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index 745a7fe118..fdb738e52e 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -28,10 +28,10 @@ module Bundler
def initialize(command, path, extra_info = nil)
@command = command
- msg = String.new
- msg << "Git error: command `#{command}` in directory #{path} has failed."
+ msg = String.new("Git error: command `#{command}`")
+ msg << " in directory #{path}" if path
+ msg << " has failed."
msg << "\n#{extra_info}" if extra_info
- msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist?
super msg
end
end
@@ -47,24 +47,28 @@ module Bundler
# All actions required by the Git source is encapsulated in this
# object.
class GitProxy
- attr_accessor :path, :uri, :ref
+ attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref
attr_writer :revision
- def initialize(path, uri, ref, revision = nil, git = nil)
+ def initialize(path, uri, options = {}, revision = nil, git = nil)
@path = path
@uri = uri
- @ref = ref
+ @branch = options["branch"]
+ @tag = options["tag"]
+ @ref = options["ref"]
+ @explicit_ref = branch || tag || ref
@revision = revision
@git = git
+ @commit_ref = nil
end
def revision
- @revision ||= find_local_revision
+ @revision ||= allowed_with_path { find_local_revision }
end
- def branch
- @branch ||= allowed_with_path do
- git("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
+ def current_branch
+ @current_branch ||= with_path do
+ git_local("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
end
end
@@ -76,36 +80,26 @@ module Bundler
end
def version
- git("--version").match(/(git version\s*)?((\.?\d+)+).*/)[2]
+ @version ||= full_version.match(/((\.?\d+)+).*/)[1]
end
def full_version
- git("--version").sub("git version", "").strip
+ @full_version ||= git_local("--version").sub(/git version\s*/, "").strip
end
def checkout
- return if path.exist? && has_revision_cached?
- extra_ref = "#{ref}:#{ref}" if ref && ref.start_with?("refs/")
-
- Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
+ return if has_revision_cached?
- configured_uri = configured_uri_for(uri).to_s
+ Bundler.ui.info "Fetching #{credential_filtered_uri}"
- unless path.exist?
- SharedHelpers.filesystem_access(path.dirname) do |p|
- FileUtils.mkdir_p(p)
- end
- git_retry "clone", "--bare", "--no-hardlinks", "--quiet", "--", configured_uri, path.to_s
- return unless extra_ref
- end
+ extra_fetch_needed = clone_needs_extra_fetch?
+ unshallow_needed = clone_needs_unshallow?
+ return unless extra_fetch_needed || unshallow_needed
- with_path do
- git_retry(*["fetch", "--force", "--quiet", "--tags", "--", configured_uri, "refs/heads/*:refs/heads/*", extra_ref].compact, :dir => path)
- end
+ git_remote_fetch(unshallow_needed ? ["--unshallow"] : depth_args)
end
def copy_to(destination, submodules = false)
- # method 1
unless File.exist?(destination.join(".git"))
begin
SharedHelpers.filesystem_access(destination.dirname) do |p|
@@ -114,7 +108,7 @@ module Bundler
SharedHelpers.filesystem_access(destination) do |p|
FileUtils.rm_rf(p)
end
- git_retry "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
+ git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination)
rescue Errno::EEXIST => e
file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1]
@@ -123,14 +117,10 @@ module Bundler
"this file and try again."
end
end
- # method 2
- git_retry "fetch", "--force", "--quiet", "--tags", path.to_s, :dir => destination
- begin
- git "reset", "--hard", @revision, :dir => destination
- rescue GitCommandError => e
- raise MissingGitRevisionError.new(e.command, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri))
- end
+ git "fetch", "--force", "--quiet", *extra_fetch_args, :dir => destination if @commit_ref
+
+ git "reset", "--hard", @revision, :dir => destination
if submodules
git_retry "submodule", "update", "--init", "--recursive", :dir => destination
@@ -142,14 +132,117 @@ module Bundler
private
- def git_null(*command, dir: nil)
- check_allowed(command)
+ def git_remote_fetch(args)
+ command = ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
+ command_with_no_credentials = check_allowed(command)
+
+ Bundler::Retry.new("`#{command_with_no_credentials}` at #{path}", [MissingGitRevisionError]).attempts do
+ out, err, status = capture(command, path)
+ return out if status.success?
+
+ if err.include?("couldn't find remote ref") || err.include?("not our ref")
+ raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri)
+ else
+ raise GitCommandError.new(command_with_no_credentials, path, err)
+ end
+ end
+ end
+
+ def clone_needs_extra_fetch?
+ return true if path.exist?
+
+ SharedHelpers.filesystem_access(path.dirname) do |p|
+ FileUtils.mkdir_p(p)
+ end
+
+ command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s]
+ command_with_no_credentials = check_allowed(command)
+
+ Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do
+ _, err, status = capture(command, nil)
+ return extra_ref if status.success?
+
+ if err.include?("Could not find remote branch") || # git up to 2.49
+ err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
+ raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
+ else
+ raise GitCommandError.new(command_with_no_credentials, path, err)
+ end
+ end
+ end
- out, status = SharedHelpers.with_clean_git_env do
- capture_and_ignore_stderr(*capture3_args_for(command, dir))
+ def clone_needs_unshallow?
+ return false unless path.join("shallow").exist?
+ return true if full_clone?
+
+ @revision && @revision != head_revision
+ end
+
+ def extra_ref
+ return false if not_pinned?
+ return true unless full_clone?
+
+ ref.start_with?("refs/")
+ end
+
+ def depth
+ return @depth if defined?(@depth)
+
+ @depth = if !supports_fetching_unreachable_refs?
+ nil
+ elsif not_pinned? || pinned_to_full_sha?
+ 1
+ elsif ref.include?("~")
+ parsed_depth = ref.split("~").last
+ parsed_depth.to_i + 1
end
+ end
+
+ def refspec
+ if commit
+ @commit_ref = "refs/#{commit}-sha"
+ return "#{commit}:#{@commit_ref}"
+ end
+
+ reference = fully_qualified_ref
+
+ reference ||= if ref.include?("~")
+ ref.split("~").first
+ elsif ref.start_with?("refs/")
+ ref
+ else
+ "refs/*"
+ end
+
+ "#{reference}:#{reference}"
+ end
+
+ def commit
+ @commit ||= pinned_to_full_sha? ? ref : @revision
+ end
+
+ def fully_qualified_ref
+ if branch
+ "refs/heads/#{branch}"
+ elsif tag
+ "refs/tags/#{tag}"
+ elsif ref.nil?
+ "refs/heads/#{current_branch}"
+ end
+ end
+
+ def not_pinned?
+ branch_option || ref.nil?
+ end
+
+ def pinned_to_full_sha?
+ ref =~ /\A\h{40}\z/
+ end
- [URICredentialsFilter.credential_filtered_string(out, uri), status]
+ def git_null(*command, dir: nil)
+ check_allowed(command)
+
+ capture(command, dir, :ignore_err => true)
end
def git_retry(*command, dir: nil)
@@ -161,51 +254,64 @@ module Bundler
end
def git(*command, dir: nil)
- command_with_no_credentials = check_allowed(command)
-
- out, status = SharedHelpers.with_clean_git_env do
- capture_and_filter_stderr(*capture3_args_for(command, dir))
+ run_command(*command, :dir => dir) do |unredacted_command|
+ check_allowed(unredacted_command)
end
+ end
- filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
-
- raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, filtered_out) unless status.success?
-
- filtered_out
+ def git_local(*command, dir: nil)
+ run_command(*command, :dir => dir) do |unredacted_command|
+ redact_and_check_presence(unredacted_command)
+ end
end
def has_revision_cached?
- return unless @revision
- with_path { git("cat-file", "-e", @revision, :dir => path) }
+ return unless @revision && path.exist?
+ git("cat-file", "-e", @revision, :dir => path)
true
rescue GitError
false
end
- def remove_cache
- FileUtils.rm_rf(path)
+ def find_local_revision
+ return head_revision if explicit_ref.nil?
+
+ find_revision_for(explicit_ref)
end
- def find_local_revision
- allowed_with_path do
- git("rev-parse", "--verify", ref || "HEAD", :dir => path).strip
- end
+ def head_revision
+ verify("HEAD")
+ end
+
+ def find_revision_for(reference)
+ verify(reference)
rescue GitCommandError => e
- raise MissingGitRevisionError.new(e.command, path, ref, URICredentialsFilter.credential_filtered_uri(uri))
+ raise MissingGitRevisionError.new(e.command, path, reference, credential_filtered_uri)
end
- # Adds credentials to the URI as Fetcher#configured_uri_for does
- def configured_uri_for(uri)
- if /https?:/ =~ uri
+ def verify(reference)
+ git("rev-parse", "--verify", reference, :dir => path).strip
+ end
+
+ # Adds credentials to the URI
+ def configured_uri
+ if /https?:/.match?(uri)
remote = Bundler::URI(uri)
config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
remote.userinfo ||= config_auth
remote.to_s
+ elsif File.exist?(uri)
+ "file://#{uri}"
else
- uri
+ uri.to_s
end
end
+ # Removes credentials from the URI
+ def credential_filtered_uri
+ URICredentialsFilter.credential_filtered_uri(uri)
+ end
+
def allow?
allowed = @git ? @git.allow_git_ops? : true
@@ -225,23 +331,41 @@ module Bundler
end
def check_allowed(command)
- require "shellwords"
- command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ command_with_no_credentials = redact_and_check_presence(command)
raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
command_with_no_credentials
end
- def capture_and_filter_stderr(*cmd)
- require "open3"
- return_value, captured_err, status = Open3.capture3(*cmd)
- Bundler.ui.warn URICredentialsFilter.credential_filtered_string(captured_err, uri) unless captured_err.empty?
- [return_value, status]
+ def redact_and_check_presence(command)
+ raise GitNotInstalledError.new unless Bundler.git_present?
+
+ require "shellwords"
+ URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ end
+
+ def run_command(*command, dir: nil)
+ command_with_no_credentials = yield(command)
+
+ out, err, status = capture(command, dir)
+
+ raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, err) unless status.success?
+
+ Bundler.ui.warn err unless err.empty?
+
+ out
end
- def capture_and_ignore_stderr(*cmd)
- require "open3"
- return_value, _, status = Open3.capture3(*cmd)
- [return_value, status]
+ def capture(cmd, dir, ignore_err: false)
+ SharedHelpers.with_clean_git_env do
+ require "open3"
+ out, err, status = Open3.capture3(*capture3_args_for(cmd, dir))
+
+ filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
+ return [filtered_out, status] if ignore_err
+
+ filtered_err = URICredentialsFilter.credential_filtered_string(err, uri)
+ [filtered_out, filtered_err, status]
+ end
end
def capture3_args_for(cmd, dir)
@@ -254,9 +378,53 @@ module Bundler
end
end
+ def extra_clone_args
+ args = depth_args
+ return [] if args.empty?
+
+ args += ["--single-branch"]
+ args.unshift("--no-tags") if supports_cloning_with_no_tags?
+
+ # If there's a locked revision, no need to clone any specific branch
+ # or tag, since we will end up checking out that locked revision
+ # anyways.
+ return args if @revision
+
+ args += ["--branch", branch_option] if branch_option
+ args
+ end
+
+ def depth_args
+ return [] if full_clone?
+
+ ["--depth", depth.to_s]
+ end
+
+ def extra_fetch_args
+ extra_args = [path.to_s, *depth_args]
+ extra_args.push(@commit_ref)
+ extra_args
+ end
+
+ def branch_option
+ branch || tag
+ end
+
+ def full_clone?
+ depth.nil?
+ end
+
def supports_minus_c?
@supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
end
+
+ def supports_fetching_unreachable_refs?
+ @supports_fetching_unreachable_refs ||= Gem::Version.new(version) >= Gem::Version.new("2.5.0")
+ end
+
+ def supports_cloning_with_no_tags?
+ @supports_cloning_with_no_tags ||= Gem::Version.new(version) >= Gem::Version.new("2.14.0-rc0")
+ end
end
end
end
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index 23531b8bd4..593da6d1a7 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -15,7 +15,6 @@ module Bundler
s.version = VERSION
s.license = "MIT"
s.platform = Gem::Platform::RUBY
- s.source = self
s.authors = ["bundler team"]
s.bindir = "exe"
s.homepage = "https://bundler.io"
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 672ecfd13b..bdfcf8274a 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -11,7 +11,7 @@ module Bundler
protected :original_path
- DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze
+ DEFAULT_GLOB = "{,*,*/*}.gemspec"
def initialize(options)
@options = options.dup
@@ -224,13 +224,13 @@ module Bundler
# Some gem authors put absolute paths in their gemspec
# and we have to save them from themselves
- spec.files = spec.files.map do |p|
- next p unless p =~ /\A#{Pathname::SEPARATOR_PAT}/
- next if File.directory?(p)
+ spec.files = spec.files.map do |path|
+ next path unless /\A#{Pathname::SEPARATOR_PAT}/.match?(path)
+ next if File.directory?(path)
begin
- Pathname.new(p).relative_path_from(gem_dir).to_s
+ Pathname.new(path).relative_path_from(gem_dir).to_s
rescue ArgumentError
- p
+ path
end
end.compact
diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb
index a70973bde7..0af28fe770 100644
--- a/lib/bundler/source/path/installer.rb
+++ b/lib/bundler/source/path/installer.rb
@@ -18,13 +18,7 @@ module Bundler
@build_args = options[:build_args] || Bundler.rubygems.build_args
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
@disable_extensions = options[:disable_extensions]
-
- if Bundler.requires_sudo?
- @tmp_dir = Bundler.tmp(spec.full_name).to_s
- @bin_dir = "#{@tmp_dir}/bin"
- else
- @bin_dir = @gem_bin_dir
- end
+ @bin_dir = @gem_bin_dir
end
def post_install
@@ -38,25 +32,10 @@ module Bundler
generate_bin unless spec.executables.empty?
run_hooks(:post_install)
- ensure
- Bundler.rm_rf(@tmp_dir) if Bundler.requires_sudo?
end
private
- def generate_bin
- super
-
- if Bundler.requires_sudo?
- SharedHelpers.filesystem_access(@gem_bin_dir) do |p|
- Bundler.mkdir_p(p)
- end
- spec.executables.each do |exe|
- Bundler.sudo "cp -R #{@bin_dir}/#{exe} #{@gem_bin_dir}"
- end
- end
- end
-
def run_hooks(type)
hooks_meth = "#{type}_hooks"
return unless Gem.respond_to?(hooks_meth)
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index a50934b315..af57acbbc2 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -7,12 +7,10 @@ module Bundler
class Rubygems < Source
autoload :Remote, File.expand_path("rubygems/remote", __dir__)
- # Use the API when installing less than X gems
- API_REQUEST_LIMIT = 500
# Ask for X gems per API request
API_REQUEST_SIZE = 50
- attr_reader :remotes, :caches
+ attr_reader :remotes
def initialize(options = {})
@options = options
@@ -21,11 +19,14 @@ module Bundler
@allow_remote = false
@allow_cached = false
@allow_local = options["allow_local"] || false
- @caches = [cache_path, *Bundler.rubygems.gem_cache]
Array(options["remotes"]).reverse_each {|r| add_remote(r) }
end
+ def caches
+ @caches ||= [cache_path, *Bundler.rubygems.gem_cache]
+ end
+
def local_only!
@specs = nil
@allow_local = true
@@ -122,6 +123,7 @@ module Bundler
end
end
alias_method :name, :identifier
+ alias_method :to_gemfile, :identifier
def specs
@specs ||= begin
@@ -145,7 +147,7 @@ module Bundler
end
if installed?(spec) && !force
- print_using_message "Using #{version_message(spec)}"
+ print_using_message "Using #{version_message(spec, options[:previous_spec])}"
return nil # no post-install message
end
@@ -160,27 +162,20 @@ module Bundler
return if Bundler.settings[:no_install]
- if requires_sudo?
- install_path = Bundler.tmp(spec.full_name)
- bin_path = install_path.join("bin")
- else
- install_path = rubygems_dir
- bin_path = Bundler.system_bindir
- end
-
- Bundler.mkdir_p bin_path, :no_sudo => true unless spec.executables.empty? || Bundler.rubygems.provides?(">= 2.7.5")
+ install_path = rubygems_dir
+ bin_path = Bundler.system_bindir
require_relative "../rubygems_gem_installer"
installer = Bundler::RubyGemsGemInstaller.at(
path,
- :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
- :install_dir => install_path.to_s,
- :bin_dir => bin_path.to_s,
+ :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
+ :install_dir => install_path.to_s,
+ :bin_dir => bin_path.to_s,
:ignore_dependencies => true,
- :wrappers => true,
- :env_shebang => true,
- :build_args => options[:build_args],
+ :wrappers => true,
+ :env_shebang => true,
+ :build_args => options[:build_args],
:bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum,
:bundler_extension_cache_path => extension_cache_path(spec)
)
@@ -209,34 +204,7 @@ module Bundler
spec.full_gem_path = installed_spec.full_gem_path
spec.loaded_from = installed_spec.loaded_from
- # SUDO HAX
- if requires_sudo?
- Bundler.rubygems.repository_subdirectories.each do |name|
- src = File.join(install_path, name, "*")
- dst = File.join(rubygems_dir, name)
- if name == "extensions" && Dir.glob(src).any?
- src = File.join(src, "*/*")
- ext_src = Dir.glob(src).first
- ext_src.gsub!(src[0..-6], "")
- dst = File.dirname(File.join(dst, ext_src))
- end
- SharedHelpers.filesystem_access(dst) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any?
- end
-
- spec.executables.each do |exe|
- SharedHelpers.filesystem_access(Bundler.system_bindir) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
- end
- end
-
spec.post_install_message
- ensure
- Bundler.rm_rf(install_path) if requires_sudo?
end
def cache(spec, custom_path = nil)
@@ -326,7 +294,7 @@ module Bundler
end
def dependency_api_available?
- api_fetchers.any?
+ @allow_remote && api_fetchers.any?
end
protected
@@ -360,9 +328,9 @@ module Bundler
def cached_path(spec)
global_cache_path = download_cache_path(spec)
- @caches << global_cache_path if global_cache_path
+ caches << global_cache_path if global_cache_path
- possibilities = @caches.map {|p| package_path(p, spec) }
+ possibilities = caches.map {|p| package_path(p, spec) }
possibilities.find {|p| File.exist?(p) }
end
@@ -371,8 +339,7 @@ module Bundler
end
def normalize_uri(uri)
- uri = uri.to_s
- uri = "#{uri}/" unless uri =~ %r{/$}
+ uri = URINormalizer.normalize_suffix(uri.to_s)
require_relative "../vendored_uri"
uri = Bundler::URI(uri)
raise ArgumentError, "The source must be an absolute URI. For example:\n" \
@@ -415,7 +382,6 @@ module Bundler
idx = @allow_local ? installed_specs.dup : Index.new
Dir["#{cache_path}/*.gem"].each do |gemfile|
- next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
idx << s
@@ -436,12 +402,11 @@ module Bundler
# gather lists from non-api sites
fetch_names(index_fetchers, nil, idx, false)
- # because ensuring we have all the gems we need involves downloading
- # the gemspecs of those gems, if the non-api sites contain more than
- # about 500 gems, we treat all sites as non-api for speed.
- allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
- Bundler.ui.debug "Need to query more than #{API_REQUEST_LIMIT} gems." \
- " Downloading full index instead..." unless allow_api
+ # legacy multi-remote sources need special logic to figure out
+ # dependency names and that logic can be very costly if one remote
+ # uses the dependency API but others don't. So use full indexes
+ # consistently in that particular case.
+ allow_api = !multiple_remotes?
fetch_names(api_fetchers, allow_api && dependency_names, idx, false)
end
@@ -475,38 +440,18 @@ module Bundler
gem_path = package_path(cache_path, spec)
return gem_path if File.exist?(gem_path)
- if requires_sudo?
- download_path = Bundler.tmp(spec.full_name)
- download_cache_path = default_cache_path_for(download_path)
- else
- download_cache_path = cache_path
- end
-
- SharedHelpers.filesystem_access(download_cache_path) do |p|
+ SharedHelpers.filesystem_access(cache_path) do |p|
FileUtils.mkdir_p(p)
end
- download_gem(spec, download_cache_path, previous_spec)
-
- if requires_sudo?
- SharedHelpers.filesystem_access(cache_path) do |p|
- Bundler.mkdir_p(p)
- end
- Bundler.sudo "mv #{package_path(download_cache_path, spec)} #{gem_path}"
- end
+ download_gem(spec, cache_path, previous_spec)
gem_path
- ensure
- Bundler.rm_rf(download_path) if requires_sudo?
end
def installed?(spec)
installed_specs[spec].any? && !spec.deleted_gem?
end
- def requires_sudo?
- Bundler.requires_sudo?
- end
-
def rubygems_dir
Bundler.bundle_path
end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index 6ea2910d18..4419695b7f 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -101,10 +101,6 @@ module Bundler
source_list_for(source).find {|s| equivalent_source?(source, s) }
end
- def get_with_fallback(source)
- get(source) || default_source
- end
-
def lock_sources
lock_other_sources + lock_rubygems_sources
end
@@ -161,11 +157,17 @@ module Bundler
end
def map_sources(replacement_sources)
- [@rubygems_sources, @path_sources, @git_sources, @plugin_sources].map do |sources|
+ rubygems, git, plugin = [@rubygems_sources, @git_sources, @plugin_sources].map do |sources|
sources.map do |source|
replacement_sources.find {|s| s == source } || source
end
end
+
+ path = @path_sources.map do |source|
+ replacement_sources.find {|s| s == (source.is_a?(Source::Gemspec) ? source.as_path_source : source) } || source
+ end
+
+ [rubygems, path, git, plugin]
end
def global_replacement_source(replacement_sources)
@@ -206,7 +208,7 @@ module Bundler
def warn_on_git_protocol(source)
return if Bundler.settings["git.allow_insecure"]
- if source.uri =~ /^git\:/
+ if /^git\:/.match?(source.uri)
Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \
"which transmits data without encryption. Disable this warning with " \
"`bundle config set --local git.allow_insecure true`, or switch to the `https` " \
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 14733269d6..21630e3a3e 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -7,8 +7,11 @@ module Bundler
include Enumerable
include TSort
- def initialize(specs)
+ attr_reader :incomplete_specs
+
+ def initialize(specs, incomplete_specs = [])
@specs = specs
+ @incomplete_specs = incomplete_specs
end
def for(dependencies, check = false, platforms = [nil])
@@ -19,7 +22,11 @@ module Bundler
loop do
break unless dep = deps.shift
- key = [dep[0].name, dep[1]]
+ name = dep[0].name
+ platform = dep[1]
+ incomplete = false
+
+ key = [name, platform]
next if handled.key?(key)
handled[key] = true
@@ -30,14 +37,19 @@ module Bundler
specs_for_dep.first.dependencies.each do |d|
next if d.type == :development
+ incomplete = true if d.name != "bundler" && lookup[d.name].empty?
deps << [d, dep[1]]
end
- elsif check
- specs << IncompleteSpecification.new(*key)
+ else
+ incomplete = true
+ end
+
+ if incomplete && check
+ @incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)]
end
end
- specs
+ specs.uniq
end
def [](key)
@@ -51,6 +63,12 @@ module Bundler
@sorted = nil
end
+ def delete(specs)
+ specs.each {|spec| @specs.delete(spec) }
+ @lookup = nil
+ @sorted = nil
+ end
+
def sort!
self
end
@@ -66,7 +84,7 @@ module Bundler
def materialize(deps)
materialized = self.for(deps, true)
- SpecSet.new(materialized)
+ SpecSet.new(materialized, incomplete_specs)
end
# Materialize for all the specs in the spec set, regardless of what platform they're for
@@ -83,17 +101,19 @@ module Bundler
end
def incomplete_ruby_specs?(deps)
- self.class.new(self.for(deps, true, [Gem::Platform::RUBY])).incomplete_specs.any?
+ return false if @specs.empty?
+
+ @incomplete_specs = []
+
+ self.for(deps, true, [Gem::Platform::RUBY])
+
+ @incomplete_specs.any?
end
def missing_specs
@specs.select {|s| s.is_a?(LazySpecification) }
end
- def incomplete_specs
- @specs.select {|s| s.is_a?(IncompleteSpecification) }
- end
-
def merge(set)
arr = sorted.dup
set.each do |set_spec|
@@ -104,10 +124,20 @@ module Bundler
SpecSet.new(arr)
end
+ def -(other)
+ SpecSet.new(to_a - other.to_a)
+ end
+
def find_by_name_and_platform(name, platform)
@specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
end
+ def delete_by_name(name)
+ @specs.reject! {|spec| spec.name == name }
+ @lookup = nil
+ @sorted = nil
+ end
+
def what_required(spec)
unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } }
return [spec]
@@ -145,7 +175,7 @@ module Bundler
cgems = extract_circular_gems(error)
raise CyclicDependencyError, "Your bundle requires gems that depend" \
" on each other, creating an infinite loop. Please remove either" \
- " gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
+ " gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
end
end
@@ -156,7 +186,7 @@ module Bundler
def lookup
@lookup ||= begin
lookup = Hash.new {|h, k| h[k] = [] }
- Index.sort_specs(@specs).reverse_each do |s|
+ @specs.each do |s|
lookup[s.name] << s
end
lookup
@@ -170,12 +200,10 @@ module Bundler
def specs_for_dependency(dep, platform)
specs_for_name = lookup[dep.name]
- if platform.nil?
- matching_specs = specs_for_name.map {|s| s.materialize_for_installation if Gem::Platform.match_spec?(s) }.compact
- GemHelpers.sort_best_platform_match(matching_specs, Bundler.local_platform)
- else
- GemHelpers.select_best_platform_match(specs_for_name, dep.force_ruby_platform ? Gem::Platform::RUBY : platform)
- end
+ target_platform = dep.force_ruby_platform ? Gem::Platform::RUBY : (platform || Bundler.local_platform)
+ matching_specs = GemHelpers.select_best_platform_match(specs_for_name, target_platform)
+ matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
+ matching_specs
end
def tsort_each_child(s)
diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler
index 77f90e735c..e290fe91eb 100644
--- a/lib/bundler/templates/Executable.bundler
+++ b/lib/bundler/templates/Executable.bundler
@@ -47,7 +47,7 @@ m = Module.new do
def lockfile
lockfile =
case File.basename(gemfile)
- when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
+ when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
@@ -72,13 +72,7 @@ m = Module.new do
bundler_gem_version = Gem::Version.new(version)
- requirement = bundler_gem_version.approximate_recommendation
-
- return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0")
-
- requirement += ".a" if bundler_gem_version.prerelease?
-
- requirement
+ bundler_gem_version.approximate_recommendation
end
def load_bundler!
diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb
deleted file mode 100644
index d2403f18b2..0000000000
--- a/lib/bundler/templates/gems.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-# gem "rails"
diff --git a/lib/bundler/templates/newgem/Cargo.toml.tt b/lib/bundler/templates/newgem/Cargo.toml.tt
new file mode 100644
index 0000000000..f5a460c9bb
--- /dev/null
+++ b/lib/bundler/templates/newgem/Cargo.toml.tt
@@ -0,0 +1,7 @@
+# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
+# a Rust project. Your extensions dependencies should be added to the Cargo.toml
+# in the ext/ directory.
+
+[workspace]
+members = ["./ext/<%= config[:name] %>"]
+resolver = "2"
diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt
index de82a63c5f..a0d2ac2826 100644
--- a/lib/bundler/templates/newgem/Gemfile.tt
+++ b/lib/bundler/templates/newgem/Gemfile.tt
@@ -9,6 +9,9 @@ gem "rake", "~> 13.0"
<%- if config[:ext] -%>
gem "rake-compiler"
+<%- if config[:ext] == 'rust' -%>
+gem "rb_sys", "~> 0.9.63"
+<%- end -%>
<%- end -%>
<%- if config[:test] -%>
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
index a60c7967ec..20eaac8a62 100644
--- a/lib/bundler/templates/newgem/README.md.tt
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -1,18 +1,20 @@
# <%= config[:constant_name] %>
-Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
+TODO: Delete this and the text below, and describe your gem
-TODO: Delete this and the text above, and describe your gem
+Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
## Installation
+TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
+
Install the gem and add to the application's Gemfile by executing:
- $ bundle add <%= config[:name] %>
+ $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
If bundler is not being used to manage dependencies, install the gem by executing:
- $ gem install <%= config[:name] %>
+ $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
## Usage
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index b02ada9b6c..b5a5c4e392 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -39,7 +39,17 @@ require "standard/rake"
<% end -%>
<% if config[:ext] -%>
-<% default_task_names.unshift(:clobber, :compile) -%>
+<% default_task_names.unshift(:compile) -%>
+<% default_task_names.unshift(:clobber) unless config[:ext] == 'rust' -%>
+<% if config[:ext] == 'rust' -%>
+require "rb_sys/extensiontask"
+
+task build: :compile
+
+RbSys::ExtensionTask.new(<%= config[:name].inspect %>) do |ext|
+ ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
+end
+<% else -%>
require "rake/extensiontask"
task build: :compile
@@ -47,6 +57,7 @@ task build: :compile
Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
end
+<% end -%>
<% end -%>
<% if default_task_names.size == 1 -%>
diff --git a/lib/bundler/templates/newgem/bin/console.tt b/lib/bundler/templates/newgem/bin/console.tt
index 08dfaaef69..c91ee65f93 100644
--- a/lib/bundler/templates/newgem/bin/console.tt
+++ b/lib/bundler/templates/newgem/bin/console.tt
@@ -7,9 +7,5 @@ require "<%= config[:namespaced_path] %>"
# 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/lib/bundler/templates/newgem/circleci/config.yml.tt b/lib/bundler/templates/newgem/circleci/config.yml.tt
index 79fd0dcc0f..f40f029bf1 100644
--- a/lib/bundler/templates/newgem/circleci/config.yml.tt
+++ b/lib/bundler/templates/newgem/circleci/config.yml.tt
@@ -3,8 +3,20 @@ jobs:
build:
docker:
- image: ruby:<%= RUBY_VERSION %>
+<%- if config[:ext] == 'rust' -%>
+ environment:
+ RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
+<%- end -%>
steps:
- checkout
+<%- if config[:ext] == 'rust' -%>
+ - run:
+ name: Install Rust/Cargo dependencies
+ command: apt-get update && apt-get install -y clang
+ - run:
+ name: Install a RubyGems version that can compile rust extensions
+ command: gem update --system '<%= ::Gem.rubygems_version %>'
+<%- end -%>
- run:
name: Run the default task
command: |
diff --git a/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
new file mode 100644
index 0000000000..c64385486e
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
@@ -0,0 +1,15 @@
+[package]
+name = <%= config[:name].inspect %>
+version = "0.1.0"
+edition = "2021"
+authors = ["<%= config[:author] %> <<%= config[:email] %>>"]
+<%- if config[:mit] -%>
+license = "MIT"
+<%- end -%>
+publish = false
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+magnus = { version = "0.6" }
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt
new file mode 100644
index 0000000000..0a0c5a3d09
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "mkmf"
+
+# Makes all symbols private by default to avoid unintended conflict
+# with other gems. To explicitly export symbols you can use RUBY_FUNC_EXPORTED
+# selectively, or entirely remove this flag.
+append_cflags("-fvisibility=hidden")
+
+create_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt
new file mode 100644
index 0000000000..e24566a17a
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "mkmf"
+require "rb_sys/mkmf"
+
+create_rust_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
deleted file mode 100644
index e918063ddf..0000000000
--- a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-require "mkmf"
-
-create_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
index 8177c4d202..bcd5148569 100644
--- a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
@@ -2,7 +2,7 @@
VALUE rb_m<%= config[:constant_array].join %>;
-void
+RUBY_FUNC_EXPORTED void
Init_<%= config[:underscored_name] %>(void)
{
rb_m<%= config[:constant_array].join %> = rb_define_module(<%= config[:constant_name].inspect %>);
diff --git a/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
new file mode 100644
index 0000000000..ba234529a3
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
@@ -0,0 +1,12 @@
+use magnus::{function, prelude::*, Error, Ruby};
+
+fn hello(subject: String) -> String {
+ format!("Hello from Rust, {subject}!")
+}
+
+#[magnus::init]
+fn init(ruby: &Ruby) -> Result<(), Error> {
+ let module = ruby.<%= config[:constant_array].map {|c| "define_module(#{c.dump})?"}.join(".") %>;
+ module.define_singleton_method("hello", function!(hello, 1))?;
+ Ok(())
+}
diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
index 1ff4b58b7b..be58dd8156 100644
--- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt
+++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
@@ -18,10 +18,20 @@ jobs:
steps:
- uses: actions/checkout@v3
+<%- if config[:ext] == 'rust' -%>
+ - name: Set up Ruby & Rust
+ uses: oxidize-rb/actions/setup-ruby-and-rust@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
+ cargo-cache: true
+ rubygems: '<%= ::Gem.rubygems_version %>'
+<%- else -%>
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
+<%- end -%>
- name: Run the default task
run: bundle exec rake
diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt
index b1c9f9986c..9b40ba5a58 100644
--- a/lib/bundler/templates/newgem/gitignore.tt
+++ b/lib/bundler/templates/newgem/gitignore.tt
@@ -12,6 +12,9 @@
*.o
*.a
mkmf.log
+<%- if config[:ext] == 'rust' -%>
+target/
+<%- end -%>
<%- end -%>
<%- if config[:test] == "rspec" -%>
diff --git a/lib/bundler/templates/newgem/gitlab-ci.yml.tt b/lib/bundler/templates/newgem/gitlab-ci.yml.tt
index 0e71ff26a4..d2e1f33736 100644
--- a/lib/bundler/templates/newgem/gitlab-ci.yml.tt
+++ b/lib/bundler/templates/newgem/gitlab-ci.yml.tt
@@ -1,9 +1,18 @@
-image: ruby:<%= RUBY_VERSION %>
+default:
+ image: ruby:<%= RUBY_VERSION %>
-before_script:
- - gem install bundler -v <%= Bundler::VERSION %>
- - bundle install
+ before_script:
+<%- if config[:ext] == 'rust' -%>
+ - apt-get update && apt-get install -y clang
+ - gem update --system '<%= ::Gem.rubygems_version %>'
+<%- end -%>
+ - gem install bundler -v <%= Bundler::VERSION %>
+ - bundle install
example_job:
+<%- if config[:ext] == 'rust' -%>
+ variables:
+ RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
+<%- end -%>
script:
- bundle exec rake
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index ceb2e9b28d..bb76680379 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -15,6 +15,9 @@ Gem::Specification.new do |spec|
spec.license = "MIT"
<%- end -%>
spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
+<%- if config[:ext] == 'rust' -%>
+ spec.required_rubygems_version = ">= <%= config[:rust_builder_required_rubygems_version] %>"
+<%- end -%>
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
@@ -26,15 +29,19 @@ Gem::Specification.new do |spec|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(__dir__) do
`git ls-files -z`.split("\x0").reject do |f|
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
+ (File.expand_path(f) == __FILE__) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
end
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
-<%- if config[:ext] -%>
+<%- if config[:ext] == 'c' -%>
spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
<%- end -%>
+<%- if config[:ext] == 'rust' -%>
+ spec.extensions = ["ext/<%= config[:underscored_name] %>/Cargo.toml"]
+<%- end -%>
# Uncomment to register a new dependency of your gem
# spec.add_dependency "example-gem", "~> 1.0"
diff --git a/lib/bundler/templates/newgem/travis.yml.tt b/lib/bundler/templates/newgem/travis.yml.tt
deleted file mode 100644
index eab16addca..0000000000
--- a/lib/bundler/templates/newgem/travis.yml.tt
+++ /dev/null
@@ -1,6 +0,0 @@
----
-language: ruby
-cache: bundler
-rvm:
- - <%= RUBY_VERSION %>
-before_install: gem install bundler -v <%= Bundler::VERSION %>
diff --git a/lib/bundler/ui/rg_proxy.rb b/lib/bundler/ui/rg_proxy.rb
index ef6def225b..b17ca65f53 100644
--- a/lib/bundler/ui/rg_proxy.rb
+++ b/lib/bundler/ui/rg_proxy.rb
@@ -12,7 +12,7 @@ module Bundler
end
def say(message)
- @ui && @ui.debug(message)
+ @ui&.debug(message)
end
end
end
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
index 384752a340..4139585c47 100644
--- a/lib/bundler/ui/shell.rb
+++ b/lib/bundler/ui/shell.rb
@@ -20,29 +20,52 @@ module Bundler
@shell.set_color(string, *color)
end
- def info(msg, newline = nil)
- tell_me(msg, nil, newline) if level("info")
+ def info(msg = nil, newline = nil)
+ return unless info?
+
+ tell_me(msg || yield, nil, newline)
end
- def confirm(msg, newline = nil)
- tell_me(msg, :green, newline) if level("confirm")
+ def confirm(msg = nil, newline = nil)
+ return unless confirm?
+
+ tell_me(msg || yield, :green, newline)
end
- def warn(msg, newline = nil, color = :yellow)
- return unless level("warn")
+ def warn(msg = nil, newline = nil, color = :yellow)
+ return unless warn?
return if @warning_history.include? msg
@warning_history << msg
- tell_err(msg, color, newline)
+ tell_err(msg || yield, color, newline)
+ end
+
+ def error(msg = nil, newline = nil, color = :red)
+ return unless error?
+
+ tell_err(msg || yield, color, newline)
+ end
+
+ def debug(msg = nil, newline = nil)
+ return unless debug?
+
+ tell_me(msg || yield, nil, newline)
+ end
+
+ def info?
+ level("info")
+ end
+
+ def confirm?
+ level("confirm")
end
- def error(msg, newline = nil, color = :red)
- return unless level("error")
- tell_err(msg, color, newline)
+ def warn?
+ level("warn")
end
- def debug(msg, newline = nil)
- tell_me(msg, nil, newline) if debug?
+ def error?
+ level("error")
end
def debug?
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
index dca1b2ac86..fa3292bdc9 100644
--- a/lib/bundler/ui/silent.rb
+++ b/lib/bundler/ui/silent.rb
@@ -13,30 +13,46 @@ module Bundler
string
end
- def info(message, newline = nil)
+ def info(message = nil, newline = nil)
end
- def confirm(message, newline = nil)
+ def confirm(message = nil, newline = nil)
end
- def warn(message, newline = nil)
+ def warn(message = nil, newline = nil)
@warnings |= [message]
end
- def error(message, newline = nil)
+ def error(message = nil, newline = nil)
end
- def debug(message, newline = nil)
+ def debug(message = nil, newline = nil)
+ end
+
+ def confirm?
+ false
+ end
+
+ def error?
+ false
end
def debug?
false
end
+ def info?
+ false
+ end
+
def quiet?
false
end
+ def warn?
+ false
+ end
+
def ask(message)
end
diff --git a/lib/bundler/uri_normalizer.rb b/lib/bundler/uri_normalizer.rb
new file mode 100644
index 0000000000..ad08593256
--- /dev/null
+++ b/lib/bundler/uri_normalizer.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Bundler
+ module URINormalizer
+ module_function
+
+ # Normalizes uri to a consistent version, either with or without trailing
+ # slash.
+ #
+ # TODO: Currently gem sources are locked with a trailing slash, while git
+ # sources are locked without a trailing slash. This should be normalized but
+ # the inconsistency is there for now to avoid changing all lockfiles
+ # including GIT sources. We could normalize this on the next major.
+ #
+ def normalize_suffix(uri, trailing_slash: true)
+ if trailing_slash
+ uri.end_with?("/") ? uri : "#{uri}/"
+ else
+ uri.end_with?("/") ? uri.delete_suffix("/") : uri
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
index 984c1c3dcb..455319efe3 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
@@ -3,7 +3,9 @@ require_relative "connection_pool/version"
class Bundler::ConnectionPool
class Error < ::RuntimeError; end
+
class PoolShuttingDownError < ::Bundler::ConnectionPool::Error; end
+
class TimeoutError < ::Timeout::Error; end
end
@@ -67,7 +69,7 @@ class Bundler::ConnectionPool
end
end
end
- alias then with
+ alias_method :then, :with
def checkout(options = {})
if ::Thread.current[@key]
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
index a7b1cf06a8..35d1d7cc35 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
@@ -49,7 +49,7 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast
end
end
- alias << push
+ alias_method :<<, :push
##
# Retrieves a connection from the stack. If a connection is available it is
@@ -74,7 +74,7 @@ class Bundler::ConnectionPool::TimedStack
return connection if connection
to_wait = deadline - current_time
- raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
+ raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0
@resource.wait(@mutex, to_wait)
end
end
@@ -87,7 +87,7 @@ class Bundler::ConnectionPool::TimedStack
# +:reload+ is +true+.
def shutdown(reload: false, &block)
- raise ArgumentError, "shutdown must receive a block" unless block_given?
+ raise ArgumentError, "shutdown must receive a block" unless block
@mutex.synchronize do
@shutdown_block = block
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
index 880170c06b..dd796d1021 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
@@ -30,7 +30,6 @@ class Bundler::ConnectionPool
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
end
- # rubocop:disable Style/MethodMissingSuper
# rubocop:disable Style/MissingRespondToMissing
if ::RUBY_VERSION >= "3.0.0"
def method_missing(name, *args, **kwargs, &block)
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb
index 8f8faf30c8..211311c069 100644
--- a/lib/bundler/vendor/fileutils/lib/fileutils.rb
+++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb
@@ -3,106 +3,184 @@
begin
require 'rbconfig'
rescue LoadError
- # for make mjit-headers
+ # for make rjit-headers
end
+# Namespace for file utility methods for copying, moving, removing, etc.
#
-# = fileutils.rb
+# == What's Here
#
-# Copyright (c) 2000-2007 Minero Aoki
+# First, what’s elsewhere. \Module \Bundler::FileUtils:
#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
+# - Inherits from {class Object}[rdoc-ref:Object].
+# - Supplements {class File}[rdoc-ref:File]
+# (but is not included or extended there).
#
-# == module Bundler::FileUtils
+# Here, module \Bundler::FileUtils provides methods that are useful for:
#
-# Namespace for several file utility methods for copying, moving, removing, etc.
+# - {Creating}[rdoc-ref:FileUtils@Creating].
+# - {Deleting}[rdoc-ref:FileUtils@Deleting].
+# - {Querying}[rdoc-ref:FileUtils@Querying].
+# - {Setting}[rdoc-ref:FileUtils@Setting].
+# - {Comparing}[rdoc-ref:FileUtils@Comparing].
+# - {Copying}[rdoc-ref:FileUtils@Copying].
+# - {Moving}[rdoc-ref:FileUtils@Moving].
+# - {Options}[rdoc-ref:FileUtils@Options].
#
-# === Module Functions
+# === Creating
#
-# require 'bundler/vendor/fileutils/lib/fileutils'
+# - ::mkdir: Creates directories.
+# - ::mkdir_p, ::makedirs, ::mkpath: Creates directories,
+# also creating ancestor directories as needed.
+# - ::link_entry: Creates a hard link.
+# - ::ln, ::link: Creates hard links.
+# - ::ln_s, ::symlink: Creates symbolic links.
+# - ::ln_sf: Creates symbolic links, overwriting if necessary.
+# - ::ln_sr: Creates symbolic links relative to targets
#
-# Bundler::FileUtils.cd(dir, **options)
-# Bundler::FileUtils.cd(dir, **options) {|dir| block }
-# Bundler::FileUtils.pwd()
-# Bundler::FileUtils.mkdir(dir, **options)
-# Bundler::FileUtils.mkdir(list, **options)
-# Bundler::FileUtils.mkdir_p(dir, **options)
-# Bundler::FileUtils.mkdir_p(list, **options)
-# Bundler::FileUtils.rmdir(dir, **options)
-# Bundler::FileUtils.rmdir(list, **options)
-# Bundler::FileUtils.ln(target, link, **options)
-# Bundler::FileUtils.ln(targets, dir, **options)
-# Bundler::FileUtils.ln_s(target, link, **options)
-# Bundler::FileUtils.ln_s(targets, dir, **options)
-# Bundler::FileUtils.ln_sf(target, link, **options)
-# Bundler::FileUtils.cp(src, dest, **options)
-# Bundler::FileUtils.cp(list, dir, **options)
-# Bundler::FileUtils.cp_r(src, dest, **options)
-# Bundler::FileUtils.cp_r(list, dir, **options)
-# Bundler::FileUtils.mv(src, dest, **options)
-# Bundler::FileUtils.mv(list, dir, **options)
-# Bundler::FileUtils.rm(list, **options)
-# Bundler::FileUtils.rm_r(list, **options)
-# Bundler::FileUtils.rm_rf(list, **options)
-# Bundler::FileUtils.install(src, dest, **options)
-# Bundler::FileUtils.chmod(mode, list, **options)
-# Bundler::FileUtils.chmod_R(mode, list, **options)
-# Bundler::FileUtils.chown(user, group, list, **options)
-# Bundler::FileUtils.chown_R(user, group, list, **options)
-# Bundler::FileUtils.touch(list, **options)
+# === Deleting
#
-# Possible <tt>options</tt> are:
+# - ::remove_dir: Removes a directory and its descendants.
+# - ::remove_entry: Removes an entry, including its descendants if it is a directory.
+# - ::remove_entry_secure: Like ::remove_entry, but removes securely.
+# - ::remove_file: Removes a file entry.
+# - ::rm, ::remove: Removes entries.
+# - ::rm_f, ::safe_unlink: Like ::rm, but removes forcibly.
+# - ::rm_r: Removes entries and their descendants.
+# - ::rm_rf, ::rmtree: Like ::rm_r, but removes forcibly.
+# - ::rmdir: Removes directories.
#
-# <tt>:force</tt> :: forced operation (rewrite files if exist, remove
-# directories if not empty, etc.);
-# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
-# performing it;
-# <tt>:preserve</tt> :: preserve object's group, user and modification
-# time on copying;
-# <tt>:noop</tt> :: no changes are made (usable in combination with
-# <tt>:verbose</tt> which will print the command to run)
+# === Querying
#
-# Each method documents the options that it honours. See also ::commands,
-# ::options and ::options_of methods to introspect which command have which
-# options.
+# - ::pwd, ::getwd: Returns the path to the working directory.
+# - ::uptodate?: Returns whether a given entry is newer than given other entries.
#
-# All methods that have the concept of a "source" file or directory can take
-# either one file or a list of files in that argument. See the method
-# documentation for examples.
+# === Setting
#
-# There are some `low level' methods, which do not accept keyword arguments:
+# - ::cd, ::chdir: Sets the working directory.
+# - ::chmod: Sets permissions for an entry.
+# - ::chmod_R: Sets permissions for an entry and its descendants.
+# - ::chown: Sets the owner and group for entries.
+# - ::chown_R: Sets the owner and group for entries and their descendants.
+# - ::touch: Sets modification and access times for entries,
+# creating if necessary.
#
-# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
-# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
-# Bundler::FileUtils.copy_stream(srcstream, deststream)
-# Bundler::FileUtils.remove_entry(path, force = false)
-# Bundler::FileUtils.remove_entry_secure(path, force = false)
-# Bundler::FileUtils.remove_file(path, force = false)
-# Bundler::FileUtils.compare_file(path_a, path_b)
-# Bundler::FileUtils.compare_stream(stream_a, stream_b)
-# Bundler::FileUtils.uptodate?(file, cmp_list)
+# === Comparing
#
-# == module Bundler::FileUtils::Verbose
+# - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
+# - ::compare_stream: Returns whether two streams are identical.
#
-# This module has all methods of Bundler::FileUtils module, but it outputs messages
-# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
-# in Bundler::FileUtils.
+# === Copying
#
-# == module Bundler::FileUtils::NoWrite
+# - ::copy_entry: Recursively copies an entry.
+# - ::copy_file: Copies an entry.
+# - ::copy_stream: Copies a stream.
+# - ::cp, ::copy: Copies files.
+# - ::cp_lr: Recursively creates hard links.
+# - ::cp_r: Recursively copies files, retaining mode, owner, and group.
+# - ::install: Recursively copies files, optionally setting mode,
+# owner, and group.
#
-# This module has all methods of Bundler::FileUtils module, but never changes
-# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
-# in Bundler::FileUtils.
+# === Moving
#
-# == module Bundler::FileUtils::DryRun
+# - ::mv, ::move: Moves entries.
#
-# This module has all methods of Bundler::FileUtils module, but never changes
-# files/directories. This equates to passing the <tt>:noop</tt> and
-# <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
+# === Options
+#
+# - ::collect_method: Returns the names of methods that accept a given option.
+# - ::commands: Returns the names of methods that accept options.
+# - ::have_option?: Returns whether a given method accepts a given option.
+# - ::options: Returns all option names.
+# - ::options_of: Returns the names of the options for a given method.
+#
+# == Path Arguments
+#
+# Some methods in \Bundler::FileUtils accept _path_ arguments,
+# which are interpreted as paths to filesystem entries:
+#
+# - If the argument is a string, that value is the path.
+# - If the argument has method +:to_path+, it is converted via that method.
+# - If the argument has method +:to_str+, it is converted via that method.
+#
+# == About the Examples
+#
+# Some examples here involve trees of file entries.
+# For these, we sometimes display trees using the
+# {tree command-line utility}[https://en.wikipedia.org/wiki/Tree_(command)],
+# which is a recursive directory-listing utility that produces
+# a depth-indented listing of files and directories.
+#
+# We use a helper method to launch the command and control the format:
+#
+# def tree(dirpath = '.')
+# command = "tree --noreport --charset=ascii #{dirpath}"
+# system(command)
+# end
+#
+# To illustrate:
+#
+# tree('src0')
+# # => src0
+# # |-- sub0
+# # | |-- src0.txt
+# # | `-- src1.txt
+# # `-- sub1
+# # |-- src2.txt
+# # `-- src3.txt
+#
+# == Avoiding the TOCTTOU Vulnerability
+#
+# For certain methods that recursively remove entries,
+# there is a potential vulnerability called the
+# {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use],
+# or TOCTTOU, vulnerability that can exist when:
+#
+# - An ancestor directory of the entry at the target path is world writable;
+# such directories include <tt>/tmp</tt>.
+# - The directory tree at the target path includes:
+#
+# - A world-writable descendant directory.
+# - A symbolic link.
+#
+# To avoid that vulnerability, you can use this method to remove entries:
+#
+# - Bundler::FileUtils.remove_entry_secure: removes recursively
+# if the target path points to a directory.
+#
+# Also available are these methods,
+# each of which calls \Bundler::FileUtils.remove_entry_secure:
+#
+# - Bundler::FileUtils.rm_r with keyword argument <tt>secure: true</tt>.
+# - Bundler::FileUtils.rm_rf with keyword argument <tt>secure: true</tt>.
+#
+# Finally, this method for moving entries calls \Bundler::FileUtils.remove_entry_secure
+# if the source and destination are on different file systems
+# (which means that the "move" is really a copy and remove):
+#
+# - Bundler::FileUtils.mv with keyword argument <tt>secure: true</tt>.
+#
+# \Method \Bundler::FileUtils.remove_entry_secure removes securely
+# by applying a special pre-process:
+#
+# - If the target path points to a directory, this method uses methods
+# {File#chown}[rdoc-ref:File#chown]
+# and {File#chmod}[rdoc-ref:File#chmod]
+# in removing directories.
+# - The owner of the target directory should be either the current process
+# or the super user (root).
+#
+# WARNING: You must ensure that *ALL* parent directories cannot be
+# moved by other untrusted users. For example, parent directories
+# should not be owned by untrusted users, and should not be world
+# writable except when the sticky bit is set.
+#
+# For details of this security vulnerability, see Perl cases:
+#
+# - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448].
+# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module Bundler::FileUtils
- VERSION = "1.4.1"
+ VERSION = "1.7.0"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -110,7 +188,13 @@ module Bundler::FileUtils
end
#
- # Returns the name of the current directory.
+ # Returns a string containing the path to the current directory:
+ #
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ #
+ # Bundler::FileUtils.getwd is an alias for Bundler::FileUtils.pwd.
+ #
+ # Related: Bundler::FileUtils.cd.
#
def pwd
Dir.pwd
@@ -120,19 +204,40 @@ module Bundler::FileUtils
alias getwd pwd
module_function :getwd
+ # Changes the working directory to the given +dir+, which
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
+ #
+ # With no block given,
+ # changes the current directory to the directory at +dir+; returns zero:
+ #
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ # Bundler::FileUtils.cd('..')
+ # Bundler::FileUtils.pwd # => "/rdoc"
+ # Bundler::FileUtils.cd('fileutils')
+ #
+ # With a block given, changes the current directory to the directory
+ # at +dir+, calls the block with argument +dir+,
+ # and restores the original current directory; returns the block's value:
#
- # Changes the current directory to the directory +dir+.
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
+ # Bundler::FileUtils.cd('..') { |arg| [arg, Bundler::FileUtils.pwd] } # => ["..", "/rdoc"]
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
#
- # If this method is called with block, resumes to the previous
- # working directory after the block execution has finished.
+ # Keyword arguments:
#
- # Bundler::FileUtils.cd('/') # change directory
+ # - <tt>verbose: true</tt> - prints an equivalent command:
#
- # Bundler::FileUtils.cd('/', verbose: true) # change directory and report it
+ # Bundler::FileUtils.cd('..')
+ # Bundler::FileUtils.cd('fileutils')
#
- # Bundler::FileUtils.cd('/') do # change directory
- # # ... # do something
- # end # return to original directory
+ # Output:
+ #
+ # cd ..
+ # cd fileutils
+ #
+ # Bundler::FileUtils.chdir is an alias for Bundler::FileUtils.cd.
+ #
+ # Related: Bundler::FileUtils.pwd.
#
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
@@ -146,11 +251,19 @@ module Bundler::FileUtils
module_function :chdir
#
- # Returns true if +new+ is newer than all +old_list+.
- # Non-existent files are older than any file.
+ # Returns +true+ if the file at path +new+
+ # is newer than all the files at paths in array +old_list+;
+ # +false+ otherwise.
+ #
+ # Argument +new+ and the elements of +old_list+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
#
- # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
- # system 'make hello.o'
+ # Bundler::FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
+ # Bundler::FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
+ #
+ # A non-existent file is considered to be infinitely old.
+ #
+ # Related: Bundler::FileUtils.touch.
#
def uptodate?(new, old_list)
return false unless File.exist?(new)
@@ -170,12 +283,39 @@ module Bundler::FileUtils
private_module_function :remove_trailing_slash
#
- # Creates one or more directories.
+ # Creates directories at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, creates a directory at each +path+ in +list+
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
+ #
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
+ # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
+ # see {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not create directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
+ # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
+ #
+ # Output:
#
- # Bundler::FileUtils.mkdir 'test'
- # Bundler::FileUtils.mkdir %w(tmp data)
- # Bundler::FileUtils.mkdir 'notexist', noop: true # Does not really create.
- # Bundler::FileUtils.mkdir 'tmp', mode: 0700
+ # mkdir tmp0 tmp1
+ # mkdir -m 700 tmp2 tmp3
+ #
+ # Raises an exception if any path points to an existing
+ # file or directory, or if for any reason a directory cannot be created.
+ #
+ # Related: Bundler::FileUtils.mkdir_p.
#
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -189,40 +329,56 @@ module Bundler::FileUtils
module_function :mkdir
#
- # Creates a directory and all its parent directories.
- # For example,
+ # Creates directories at the paths in the given +list+
+ # (a single path or an array of paths),
+ # also creating ancestor directories as needed;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, creates a directory at each +path+ in +list+,
+ # along with any needed ancestor directories,
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
+ #
+ # Bundler::FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
+ # Bundler::FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
+ # see {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not create directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
+ # Bundler::FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
#
- # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
+ # Output:
#
- # causes to make following directories, if they do not exist.
+ # mkdir -p tmp0 tmp1
+ # mkdir -p -m 700 tmp2 tmp3
#
- # * /usr
- # * /usr/local
- # * /usr/local/lib
- # * /usr/local/lib/ruby
+ # Raises an exception if for any reason a directory cannot be created.
#
- # You can pass several directories at a time in a list.
+ # Bundler::FileUtils.mkpath and Bundler::FileUtils.makedirs are aliases for Bundler::FileUtils.mkdir_p.
+ #
+ # Related: Bundler::FileUtils.mkdir.
#
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
- list.map {|path| remove_trailing_slash(path)}.each do |path|
- # optimize for the most common case
- begin
- fu_mkdir path, mode
- next
- rescue SystemCallError
- next if File.directory?(path)
- end
+ list.each do |item|
+ path = remove_trailing_slash(item)
stack = []
- until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
+ until File.directory?(path) || File.dirname(path) == path
stack.push path
path = File.dirname(path)
end
- stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
@@ -253,12 +409,39 @@ module Bundler::FileUtils
private_module_function :fu_mkdir
#
- # Removes one or more directories.
+ # Removes directories at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, removes the directory at each +path+ in +list+,
+ # by calling: <tt>Dir.rmdir(path)</tt>;
+ # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]:
+ #
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
+ # Bundler::FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>parents: true</tt> - removes successive ancestor directories
+ # if empty.
+ # - <tt>noop: true</tt> - does not remove directories.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
#
- # Bundler::FileUtils.rmdir 'somedir'
- # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
- # # Does not really remove directory; outputs message.
- # Bundler::FileUtils.rmdir 'somedir', verbose: true, noop: true
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
+ # Bundler::FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
+ #
+ # Output:
+ #
+ # rmdir -p tmp0/tmp1 tmp2/tmp3
+ # rmdir -p tmp4/tmp5
+ #
+ # Raises an exception if a directory does not exist
+ # or if for any reason a directory cannot be removed.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -279,26 +462,62 @@ module Bundler::FileUtils
end
module_function :rmdir
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # When +src+ is the path to an existing file
+ # and +dest+ is the path to a non-existent file,
+ # creates a hard link at +dest+ pointing to +src+; returns zero:
+ #
+ # Dir.children('tmp0/') # => ["t.txt"]
+ # Dir.children('tmp1/') # => []
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
+ # Dir.children('tmp1/') # => ["t.lnk"]
+ #
+ # When +src+ is the path to an existing file
+ # and +dest+ is the path to an existing directory,
+ # creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
#
- # :call-seq:
- # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
+ # Dir.children('tmp2') # => ["t.dat"]
+ # Dir.children('tmp3') # => []
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
+ # Dir.children('tmp3') # => ["t.dat"]
#
- # In the first form, creates a hard link +link+ which points to +target+.
- # If +link+ already exists, raises Errno::EEXIST.
- # But if the +force+ option is set, overwrites +link+.
+ # When +src+ is an array of paths to existing files
+ # and +dest+ is the path to an existing directory,
+ # then for each path +target+ in +src+,
+ # creates a hard link at <tt>dest/target</tt> pointing to +target+;
+ # returns +src+:
#
- # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
- # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+ # Dir.children('tmp4/') # => []
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
+ # Dir.children('tmp4/') # => ["t.dat", "t.txt"]
#
- # In the second form, creates a link +dir/target+ pointing to +target+.
- # In the third form, creates several hard links in the directory +dir+,
- # pointing to each item in +targets+.
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ # Keyword arguments:
#
- # Bundler::FileUtils.cd '/sbin'
- # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
+ #
+ # Output:
+ #
+ # ln tmp0/t.txt tmp1/t.lnk
+ # ln tmp2/t.dat tmp3
+ # ln tmp0/t.txt tmp2/t.dat tmp4/
+ #
+ # Raises an exception if +dest+ is the path to an existing file
+ # and keyword argument +force+ is not +true+.
+ #
+ # Bundler::FileUtils#link is an alias for Bundler::FileUtils#ln.
+ #
+ # Related: Bundler::FileUtils.link_entry (has different options).
#
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -313,28 +532,103 @@ module Bundler::FileUtils
alias link ln
module_function :link
- #
- # Hard link +src+ to +dest+. If +src+ is a directory, this method links
- # all its contents recursively. If +dest+ is a directory, links
- # +src+ to +dest/src+.
- #
- # +src+ can be a list of files.
- #
- # If +dereference_root+ is true, this method dereference tree root.
- #
- # If +remove_destination+ is true, this method removes each destination file before copy.
- #
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
- # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
- #
- # # Examples of linking several files to target directory.
- # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
- # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
- #
- # # If you want to link all contents of a directory instead of the
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
- # # use the following code.
- # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # creates links +dest+ and descendents pointing to +src+ and its descendents:
+ #
+ # tree('src0')
+ # # => src0
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # File.exist?('dest0') # => false
+ # Bundler::FileUtils.cp_lr('src0', 'dest0')
+ # tree('dest0')
+ # # => dest0
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ and +dest+ are both paths to directories,
+ # creates links <tt>dest/src</tt> and descendents
+ # pointing to +src+ and its descendents:
+ #
+ # tree('src1')
+ # # => src1
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp_lr('src1', 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # `-- src1
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ is an array of paths to entries and +dest+ is the path to a directory,
+ # for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
+ # pointing to that path:
+ #
+ # tree('src2')
+ # # => src2
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest2')
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
+ # tree('dest2')
+ # # => dest2
+ # # |-- sub0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- sub1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
+ # does not dereference it.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp -lr src0 dest0
+ # cp -lr src1 dest1
+ # cp -lr src2/sub0 src2/sub1 dest2
+ #
+ # Raises an exception if +dest+ is the path to an existing file or directory
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp_lr(src, dest, noop: nil, verbose: nil,
dereference_root: true, remove_destination: false)
@@ -346,27 +640,81 @@ module Bundler::FileUtils
end
module_function :cp_lr
+ # Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to an existing file:
+ #
+ # - When +dest+ is the path to a non-existent file,
+ # creates a symbolic link at +dest+ pointing to +src+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt')
+ # File.symlink?('dest0.txt') # => true
#
- # :call-seq:
- # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
- # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
+ # - When +dest+ is the path to an existing file,
+ # creates a symbolic link at +dest+ pointing to +src+
+ # if and only if keyword argument <tt>force: true</tt> is given
+ # (raises an exception otherwise):
#
- # In the first form, creates a symbolic link +link+ which points to +target+.
- # If +link+ already exists, raises Errno::EEXIST.
- # But if the <tt>force</tt> option is set, overwrites +link+.
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.touch('dest1.txt')
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
+ # FileTest.symlink?('dest1.txt') # => true
#
- # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
- # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
#
- # In the second form, creates a link +dir/target+ pointing to +target+.
- # In the third form, creates several symbolic links in the directory +dir+,
- # pointing to each item in +targets+.
- # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ # If +dest+ is the path to a directory,
+ # creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
#
- # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
+ # Bundler::FileUtils.touch('src2.txt')
+ # Bundler::FileUtils.mkdir('destdir2')
+ # Bundler::FileUtils.ln_s('src2.txt', 'destdir2')
+ # File.symlink?('destdir2/src2.txt') # => true
#
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ # If +src+ is an array of paths to existing files and +dest+ is a directory,
+ # for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
+ # pointing to +child+:
+ #
+ # Bundler::FileUtils.mkdir('srcdir3')
+ # Bundler::FileUtils.touch('srcdir3/src0.txt')
+ # Bundler::FileUtils.touch('srcdir3/src1.txt')
+ # Bundler::FileUtils.mkdir('destdir3')
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
+ # File.symlink?('destdir3/src0.txt') # => true
+ # File.symlink?('destdir3/src1.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>relative: false</tt> - create links relative to +dest+.
+ # - <tt>noop: true</tt> - does not create links.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # ln -s src0.txt dest0.txt
+ # ln -s src1.txt destdir1
+ # ln -sf src2.txt dest2.txt
+ # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
+ #
+ # Bundler::FileUtils.symlink is an alias for Bundler::FileUtils.ln_s.
+ #
+ # Related: Bundler::FileUtils.ln_sf.
+ #
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
+ if relative
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
+ end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
@@ -379,29 +727,95 @@ module Bundler::FileUtils
alias symlink ln_s
module_function :symlink
- #
- # :call-seq:
- # Bundler::FileUtils.ln_sf(*args)
- #
- # Same as
- #
- # Bundler::FileUtils.ln_s(*args, force: true)
+ # Like Bundler::FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
#
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
end
module_function :ln_sf
+ # Like Bundler::FileUtils.ln_s, but create links relative to +dest+.
+ #
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
+ dest = File.path(dest)
+ srcs = Array(src)
+ link = proc do |s, target_dir_p = true|
+ s = File.path(s)
+ if target_dir_p
+ d = File.join(destdirs = dest, File.basename(s))
+ else
+ destdirs = File.dirname(d = dest)
+ end
+ destdirs = fu_split_path(File.realpath(destdirs))
+ if fu_starting_path?(s)
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
+ base = fu_relative_components_from(srcdirs, destdirs)
+ s = File.join(*base)
+ else
+ srcdirs = fu_clean_components(*fu_split_path(s))
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
+ srcdirs.shift
+ base.pop
+ end
+ s = File.join(*base, *srcdirs)
+ end
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
+ next if noop
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ case srcs.size
+ when 0
+ when 1
+ link[srcs[0], target_directory && File.directory?(dest)]
+ else
+ srcs.each(&link)
+ end
+ end
+ module_function :ln_sr
+
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
#
- # Hard links a file system entry +src+ to +dest+.
- # If +src+ is a directory, this method links its contents recursively.
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Both of +src+ and +dest+ must be a path name.
- # +src+ must exist, +dest+ must not exist.
+ # If +src+ is the path to a file and +dest+ does not exist,
+ # creates a hard link at +dest+ pointing to +src+:
#
- # If +dereference_root+ is true, this method dereferences the tree root.
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.link_entry('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # If +remove_destination+ is true, this method removes each destination file before copy.
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # recursively creates hard links at +dest+ pointing to paths in +src+:
+ #
+ # Bundler::FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
+ # src_file_paths = [
+ # 'src1/dir0/t0.txt',
+ # 'src1/dir0/t1.txt',
+ # 'src1/dir1/t2.txt',
+ # 'src1/dir1/t3.txt',
+ # ]
+ # Bundler::FileUtils.touch(src_file_paths)
+ # File.directory?('dest1') # => true
+ # Bundler::FileUtils.link_entry('src1', 'dest1')
+ # File.file?('dest1/dir0/t0.txt') # => true
+ # File.file?('dest1/dir0/t1.txt') # => true
+ # File.file?('dest1/dir1/t2.txt') # => true
+ # File.file?('dest1/dir1/t3.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
+ #
+ # Raises an exception if +dest+ is the path to an existing file or directory
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
+ #
+ # Related: Bundler::FileUtils.ln (has different options).
#
def link_entry(src, dest, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).traverse do |ent|
@@ -412,16 +826,59 @@ module Bundler::FileUtils
end
module_function :link_entry
+ # Copies files.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
+ # copies +src+ to +dest+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # Copies a file content +src+ to +dest+. If +dest+ is a directory,
- # copies +src+ to +dest/src+.
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
+ # copies +src+ to <tt>dest/src</tt>:
#
- # If +src+ is a list of files, then +dest+ must be a directory.
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp('src1.txt', 'dest1')
+ # File.file?('dest1/src1.txt') # => true
#
- # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
- # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true
- # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
+ # If +src+ is an array of paths to files and +dest+ is the path to a directory,
+ # copies from each +src+ to +dest+:
+ #
+ # src_file_paths = ['src2.txt', 'src2.dat']
+ # Bundler::FileUtils.touch(src_file_paths)
+ # Bundler::FileUtils.mkdir('dest2')
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2')
+ # File.file?('dest2/src2.txt') # => true
+ # File.file?('dest2/src2.dat') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>noop: true</tt> - does not copy files.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp src0.txt dest0.txt
+ # cp src1.txt dest1
+ # cp src2.txt src2.dat dest2
+ #
+ # Raises an exception if +src+ is a directory.
+ #
+ # Bundler::FileUtils.copy is an alias for Bundler::FileUtils.cp.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -435,30 +892,105 @@ module Bundler::FileUtils
alias copy cp
module_function :copy
- #
- # Copies +src+ to +dest+. If +src+ is a directory, this method copies
- # all its contents recursively. If +dest+ is a directory, copies
- # +src+ to +dest/src+.
- #
- # +src+ can be a list of files.
- #
- # If +dereference_root+ is true, this method dereference tree root.
- #
- # If +remove_destination+ is true, this method removes each destination file before copy.
- #
- # # Installing Ruby library "mylib" under the site_ruby
- # Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
- # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
- #
- # # Examples of copying several files to target directory.
- # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
- # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
- #
- # # If you want to copy all contents of a directory instead of the
- # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
- # # use following code.
- # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
- # # but this doesn't.
+ # Recursively copies files.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # The mode, owner, and group are retained in the copy;
+ # to change those, use Bundler::FileUtils.install instead.
+ #
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
+ # copies +src+ to +dest+:
+ #
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
+ #
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
+ # copies +src+ to <tt>dest/src</tt>:
+ #
+ # Bundler::FileUtils.touch('src1.txt')
+ # Bundler::FileUtils.mkdir('dest1')
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1')
+ # File.file?('dest1/src1.txt') # => true
+ #
+ # If +src+ is the path to a directory and +dest+ does not exist,
+ # recursively copies +src+ to +dest+:
+ #
+ # tree('src2')
+ # # => src2
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.exist?('dest2') # => false
+ # Bundler::FileUtils.cp_r('src2', 'dest2')
+ # tree('dest2')
+ # # => dest2
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ and +dest+ are paths to directories,
+ # recursively copies +src+ to <tt>dest/src</tt>:
+ #
+ # tree('src3')
+ # # => src3
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.mkdir('dest3')
+ # Bundler::FileUtils.cp_r('src3', 'dest3')
+ # tree('dest3')
+ # # => dest3
+ # # `-- src3
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ #
+ # If +src+ is an array of paths and +dest+ is a directory,
+ # recursively copies from each path in +src+ to +dest+;
+ # the paths in +src+ may point to files and/or directories.
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
+ # does not dereference it.
+ # - <tt>noop: true</tt> - does not copy files.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
+ # Bundler::FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # cp -r src0.txt dest0.txt
+ # cp -r src1.txt dest1
+ # cp -r src2 dest2
+ # cp -r src3 dest3
+ #
+ # Raises an exception of +src+ is the path to a directory
+ # and +dest+ is the path to a file.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
@@ -470,21 +1002,50 @@ module Bundler::FileUtils
end
module_function :cp_r
+ # Recursively copies files from +src+ to +dest+.
+ #
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Copies a file system entry +src+ to +dest+.
- # If +src+ is a directory, this method copies its contents recursively.
- # This method preserves file types, c.f. symlink, directory...
- # (FIFO, device files and etc. are not supported yet)
+ # If +src+ is the path to a file, copies +src+ to +dest+:
#
- # Both of +src+ and +dest+ must be a path name.
- # +src+ must exist, +dest+ must not exist.
+ # Bundler::FileUtils.touch('src0.txt')
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.copy_entry('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
#
- # If +preserve+ is true, this method preserves owner, group, and
- # modified time. Permissions are copied regardless +preserve+.
+ # If +src+ is a directory, recursively copies +src+ to +dest+:
#
- # If +dereference_root+ is true, this method dereference tree root.
+ # tree('src1')
+ # # => src1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.copy_entry('src1', 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
#
- # If +remove_destination+ is true, this method removes each destination file before copy.
+ # The recursive copying preserves file types for regular files,
+ # directories, and symbolic links;
+ # other file types (FIFO streams, device files, etc.) are not supported.
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
+ # follows the link.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
if dereference_root
@@ -502,9 +1063,25 @@ module Bundler::FileUtils
end
module_function :copy_entry
+ # Copies file from +src+ to +dest+, which should not be directories.
+ #
+ # Arguments +src+ and +dest+
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Examples:
#
- # Copies file contents of +src+ to +dest+.
- # Both of +src+ and +dest+ must be a path name.
+ # Bundler::FileUtils.touch('src0.txt')
+ # Bundler::FileUtils.copy_file('src0.txt', 'dest0.txt')
+ # File.file?('dest0.txt') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>dereference: false</tt> - if +src+ is a symbolic link,
+ # does not follow the link.
+ # - <tt>preserve: true</tt> - preserves file times.
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_file(src, dest, preserve = false, dereference = true)
ent = Entry_.new(src, nil, dereference)
@@ -513,25 +1090,81 @@ module Bundler::FileUtils
end
module_function :copy_file
+ # Copies \IO stream +src+ to \IO stream +dest+ via
+ # {IO.copy_stream}[rdoc-ref:IO.copy_stream].
#
- # Copies stream +src+ to +dest+.
- # +src+ must respond to #read(n) and
- # +dest+ must respond to #write(str).
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def copy_stream(src, dest)
IO.copy_stream(src, dest)
end
module_function :copy_stream
- #
- # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
- # disk partition, the file is copied then the original file is removed.
- #
- # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
- # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
- #
- # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
- # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
+ # Moves entries.
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # If +src+ and +dest+ are on different file systems,
+ # first copies, then removes +src+.
+ #
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # If +src+ is the path to a single file or directory and +dest+ does not exist,
+ # moves +src+ to +dest+:
+ #
+ # tree('src0')
+ # # => src0
+ # # |-- src0.txt
+ # # `-- src1.txt
+ # File.exist?('dest0') # => false
+ # Bundler::FileUtils.mv('src0', 'dest0')
+ # File.exist?('src0') # => false
+ # tree('dest0')
+ # # => dest0
+ # # |-- src0.txt
+ # # `-- src1.txt
+ #
+ # If +src+ is an array of paths to files and directories
+ # and +dest+ is the path to a directory,
+ # copies from each path in the array to +dest+:
+ #
+ # File.file?('src1.txt') # => true
+ # tree('src1')
+ # # => src1
+ # # |-- src.dat
+ # # `-- src.txt
+ # Dir.empty?('dest1') # => true
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1')
+ # tree('dest1')
+ # # => dest1
+ # # |-- src1
+ # # | |-- src.dat
+ # # | `-- src.txt
+ # # `-- src1.txt
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - if the move includes removing +src+
+ # (that is, if +src+ and +dest+ are on different file systems),
+ # ignores raised exceptions of StandardError and its descendants.
+ # - <tt>noop: true</tt> - does not move files.
+ # - <tt>secure: true</tt> - removes +src+ securely;
+ # see details at Bundler::FileUtils.remove_entry_secure.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # mv src0 dest0
+ # mv src1.txt src1 dest1
+ #
+ # Bundler::FileUtils.move is an alias for Bundler::FileUtils.mv.
#
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -565,13 +1198,34 @@ module Bundler::FileUtils
alias move mv
module_function :move
+ # Removes entries at the paths in the given +list+
+ # (a single path or an array of paths)
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # With no keyword arguments, removes files at the paths given in +list+:
+ #
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
+ #
+ # Keyword arguments:
+ #
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
+ # and its descendants.
+ # - <tt>noop: true</tt> - does not remove files; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
#
- # Remove file(s) specified in +list+. This method cannot remove directories.
- # All StandardErrors are ignored when the :force option is set.
+ # Output:
#
- # Bundler::FileUtils.rm %w( junk.txt dust.txt )
- # Bundler::FileUtils.rm Dir.glob('*.so')
- # Bundler::FileUtils.rm 'NotExistFile', force: true # never raises exception
+ # rm src0.dat src0.txt
+ #
+ # Bundler::FileUtils.remove is an alias for Bundler::FileUtils.rm.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
@@ -587,10 +1241,18 @@ module Bundler::FileUtils
alias remove rm
module_function :remove
+ # Equivalent to:
+ #
+ # Bundler::FileUtils.rm(list, force: true, **kwargs)
+ #
+ # Argument +list+ (a single path or an array of paths)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # See Bundler::FileUtils.rm for keyword arguments.
#
- # Equivalent to
+ # Bundler::FileUtils.safe_unlink is an alias for Bundler::FileUtils.rm_f.
#
- # Bundler::FileUtils.rm(list, force: true)
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
@@ -600,24 +1262,55 @@ module Bundler::FileUtils
alias safe_unlink rm_f
module_function :safe_unlink
+ # Removes entries at the paths in the given +list+
+ # (a single path or an array of paths);
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # For each file path, removes the file at that path:
+ #
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'])
+ # File.exist?('src0.txt') # => false
+ # File.exist?('src0.dat') # => false
#
- # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
- # removes its all contents recursively. This method ignores
- # StandardError when :force option is set.
+ # For each directory path, recursively removes files and directories:
#
- # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
- # Bundler::FileUtils.rm_r 'some_dir', force: true
+ # tree('src1')
+ # # => src1
+ # # |-- dir0
+ # # | |-- src0.txt
+ # # | `-- src1.txt
+ # # `-- dir1
+ # # |-- src2.txt
+ # # `-- src3.txt
+ # Bundler::FileUtils.rm_r('src1')
+ # File.exist?('src1') # => false
#
- # WARNING: This method causes local vulnerability
- # if one of parent directories or removing directory tree are world
- # writable (including /tmp, whose permission is 1777), and the current
- # process has strong privilege such as Unix super user (root), and the
- # system has symbolic link. For secure removing, read the documentation
- # of remove_entry_secure carefully, and set :secure option to true.
- # Default is <tt>secure: false</tt>.
+ # Keyword arguments:
#
- # NOTE: This method calls remove_entry_secure if :secure option is set.
- # See also remove_entry_secure.
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
+ # and its descendants.
+ # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
+ # - <tt>secure: true</tt> - removes +src+ securely;
+ # see details at Bundler::FileUtils.remove_entry_secure.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
+ # Bundler::FileUtils.rm_r('src1', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # rm -r src0.dat src0.txt
+ # rm -r src1
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
@@ -633,13 +1326,22 @@ module Bundler::FileUtils
end
module_function :rm_r
+ # Equivalent to:
#
- # Equivalent to
+ # Bundler::FileUtils.rm_r(list, force: true, **kwargs)
#
- # Bundler::FileUtils.rm_r(list, force: true)
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # WARNING: This method causes local vulnerability.
- # Read the documentation of rm_r first.
+ # May cause a local vulnerability if not called with keyword argument
+ # <tt>secure: true</tt>;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
+ #
+ # See Bundler::FileUtils.rm_r for keyword arguments.
+ #
+ # Bundler::FileUtils.rmtree is an alias for Bundler::FileUtils.rm_rf.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
@@ -649,37 +1351,20 @@ module Bundler::FileUtils
alias rmtree rm_rf
module_function :rmtree
+ # Securely removes the entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
#
- # This method removes a file system entry +path+. +path+ shall be a
- # regular file, a directory, or something. If +path+ is a directory,
- # remove it recursively. This method is required to avoid TOCTTOU
- # (time-of-check-to-time-of-use) local security vulnerability of rm_r.
- # #rm_r causes security hole when:
- #
- # * Parent directory is world writable (including /tmp).
- # * Removing directory tree includes world writable directory.
- # * The system has symbolic link.
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # To avoid this security hole, this method applies special preprocess.
- # If +path+ is a directory, this method chown(2) and chmod(2) all
- # removing directories. This requires the current process is the
- # owner of the removing whole directory tree, or is the super user (root).
+ # Avoids a local vulnerability that can exist in certain circumstances;
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
#
- # WARNING: You must ensure that *ALL* parent directories cannot be
- # moved by other untrusted users. For example, parent directories
- # should not be owned by untrusted users, and should not be world
- # writable except when the sticky bit set.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
#
- # WARNING: Only the owner of the removing directory tree, or Unix super
- # user (root) should invoke this method. Otherwise this method does not
- # work.
- #
- # For details of this security vulnerability, see Perl's case:
- #
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
- # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
- #
- # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
@@ -767,12 +1452,17 @@ module Bundler::FileUtils
end
private_module_function :fu_stat_identical_entry?
+ # Removes the entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
#
- # This method removes a file system entry +path+.
- # +path+ might be a regular file, a directory, or something.
- # If +path+ is a directory, remove it recursively.
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # See also remove_entry_secure.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: Bundler::FileUtils.remove_entry_secure.
#
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
@@ -787,9 +1477,16 @@ module Bundler::FileUtils
end
module_function :remove_entry
+ # Removes the file entry given by +path+,
+ # which should be the entry for a regular file or a symbolic link.
+ #
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Removes a file +path+.
- # This method ignores StandardError if +force+ is true.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_file(path, force = false)
Entry_.new(path).remove_file
@@ -798,20 +1495,32 @@ module Bundler::FileUtils
end
module_function :remove_file
+ # Recursively removes the directory entry given by +path+,
+ # which should be the entry for a regular file, a symbolic link,
+ # or a directory.
+ #
+ # Argument +path+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Removes a directory +dir+ and its contents recursively.
- # This method ignores StandardError if +force+ is true.
+ # Optional argument +force+ specifies whether to ignore
+ # raised exceptions of StandardError and its descendants.
+ #
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def remove_dir(path, force = false)
remove_entry path, force # FIXME?? check if it is a directory
end
module_function :remove_dir
+ # Returns +true+ if the contents of files +a+ and +b+ are identical,
+ # +false+ otherwise.
+ #
+ # Arguments +a+ and +b+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Returns true if the contents of a file +a+ and a file +b+ are identical.
+ # Bundler::FileUtils.identical? and Bundler::FileUtils.cmp are aliases for Bundler::FileUtils.compare_file.
#
- # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
- # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
+ # Related: Bundler::FileUtils.compare_stream.
#
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
@@ -828,19 +1537,19 @@ module Bundler::FileUtils
module_function :identical?
module_function :cmp
+ # Returns +true+ if the contents of streams +a+ and +b+ are identical,
+ # +false+ otherwise.
#
- # Returns true if the contents of a stream +a+ and +b+ are identical.
+ # Arguments +a+ and +b+
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Related: Bundler::FileUtils.compare_file.
#
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
- if RUBY_VERSION > "2.4"
- sa = String.new(capacity: bsize)
- sb = String.new(capacity: bsize)
- else
- sa = String.new
- sb = String.new
- end
+ sa = String.new(capacity: bsize)
+ sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
@@ -851,13 +1560,69 @@ module Bundler::FileUtils
end
module_function :compare_stream
+ # Copies a file entry.
+ # See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
+ #
+ # Arguments +src+ (a single path or an array of paths)
+ # and +dest+ (a single path)
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
+ #
+ # If the entry at +dest+ does not exist, copies from +src+ to +dest+:
+ #
+ # File.read('src0.txt') # => "aaa\n"
+ # File.exist?('dest0.txt') # => false
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt')
+ # File.read('dest0.txt') # => "aaa\n"
+ #
+ # If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
+ #
+ # File.read('src1.txt') # => "aaa\n"
+ # File.read('dest1.txt') # => "bbb\n"
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt')
+ # File.read('dest1.txt') # => "aaa\n"
#
- # If +src+ is not same as +dest+, copies it and changes the permission
- # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
- # This method removes destination before copy.
+ # If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
+ # overwriting if necessary:
#
- # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true
- # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
+ # File.read('src2.txt') # => "aaa\n"
+ # File.read('dest2/src2.txt') # => "bbb\n"
+ # Bundler::FileUtils.install('src2.txt', 'dest2')
+ # File.read('dest2/src2.txt') # => "aaa\n"
+ #
+ # If +src+ is an array of paths and +dest+ points to a directory,
+ # copies each path +path+ in +src+ to <tt>dest/path</tt>:
+ #
+ # File.file?('src3.txt') # => true
+ # File.file?('src3.dat') # => true
+ # Bundler::FileUtils.mkdir('dest3')
+ # Bundler::FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
+ # File.file?('dest3/src3.txt') # => true
+ # File.file?('dest3/src3.dat') # => true
+ #
+ # Keyword arguments:
+ #
+ # - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
+ # using {File.chown}[rdoc-ref:File.chown].
+ # - <tt>mode: <i>permissions</i></tt> - changes the permissions.
+ # using {File.chmod}[rdoc-ref:File.chmod].
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
+ # - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
+ # using {File.chown}[rdoc-ref:File.chown].
+ # - <tt>preserve: true</tt> - preserve timestamps
+ # using {File.utime}[rdoc-ref:File.utime].
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # install -c src0.txt dest0.txt
+ # install -c src1.txt dest1.txt
+ # install -c src2.txt dest2
+ #
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
@@ -917,11 +1682,8 @@ module Bundler::FileUtils
private_module_function :apply_mask
def symbolic_modes_to_i(mode_sym, path) #:nodoc:
- mode = if File::Stat === path
- path.mode
- else
- File.stat(path).mode
- end
+ path = File.stat(path) unless File::Stat === path
+ mode = path.mode
mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
target, *actions = clause.split(/([=+-])/)
raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
@@ -938,7 +1700,7 @@ module Bundler::FileUtils
when "x"
mask | 0111
when "X"
- if FileTest.directory? path
+ if path.directory?
mask | 0111
else
mask
@@ -978,37 +1740,78 @@ module Bundler::FileUtils
end
private_module_function :mode_to_s
+ # Changes permissions on the entries at the paths given in +list+
+ # (a single path or an array of paths)
+ # to the permissions given by +mode+;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
+ #
+ # - Modifies each entry that is a regular file using
+ # {File.chmod}[rdoc-ref:File.chmod].
+ # - Modifies each entry that is a symbolic link using
+ # {File.lchmod}[rdoc-ref:File.lchmod].
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Argument +mode+ may be either an integer or a string:
+ #
+ # - \Integer +mode+: represents the permission bits to be set:
+ #
+ # Bundler::FileUtils.chmod(0755, 'src0.txt')
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
+ #
+ # - \String +mode+: represents the permissions to be set:
+ #
+ # The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
+ #
+ # - +targets+ may be any combination of these letters:
+ #
+ # - <tt>'u'</tt>: permissions apply to the file's owner.
+ # - <tt>'g'</tt>: permissions apply to users in the file's group.
+ # - <tt>'o'</tt>: permissions apply to other users not in the file's group.
+ # - <tt>'a'</tt> (the default): permissions apply to all users.
+ #
+ # - +operator+ may be one of these letters:
+ #
+ # - <tt>'+'</tt>: adds permissions.
+ # - <tt>'-'</tt>: removes permissions.
+ # - <tt>'='</tt>: sets (replaces) permissions.
+ #
+ # - +perms+ (may be repeated, with separating commas)
+ # may be any combination of these letters:
+ #
+ # - <tt>'r'</tt>: Read.
+ # - <tt>'w'</tt>: Write.
+ # - <tt>'x'</tt>: Execute (search, for a directory).
+ # - <tt>'X'</tt>: Search (for a directories only;
+ # must be used with <tt>'+'</tt>)
+ # - <tt>'s'</tt>: Uid or gid.
+ # - <tt>'t'</tt>: Sticky bit.
+ #
+ # Examples:
+ #
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
+ #
+ # Keyword arguments:
+ #
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # chmod 755 src0.txt
+ # chmod 644 src0.txt src0.dat
+ # chmod u=wrx,go=rx src1.txt
+ # chmod u=wrx,go=rx /usr/bin/ruby
+ #
+ # Related: Bundler::FileUtils.chmod_R.
#
- # Changes permission bits on the named files (in +list+) to the bit pattern
- # represented by +mode+.
- #
- # +mode+ is the symbolic and absolute mode can be used.
- #
- # Absolute mode is
- # Bundler::FileUtils.chmod 0755, 'somecommand'
- # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
- # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
- #
- # Symbolic mode is
- # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
- # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
- # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
- #
- # "a" :: is user, group, other mask.
- # "u" :: is user's mask.
- # "g" :: is group's mask.
- # "o" :: is other's mask.
- # "w" :: is write permission.
- # "r" :: is read permission.
- # "x" :: is execute permission.
- # "X" ::
- # is execute permission for directories only, must be used in conjunction with "+"
- # "s" :: is uid, gid.
- # "t" :: is sticky bit.
- # "+" :: is added to a class given the specified mode.
- # "-" :: Is removed from a given class given mode.
- # "=" :: Is the exact nature of the class will be given a specified mode.
-
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
@@ -1019,12 +1822,7 @@ module Bundler::FileUtils
end
module_function :chmod
- #
- # Changes permission bits on the named files (in +list+)
- # to the bit pattern represented by +mode+.
- #
- # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
- # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
+ # Like Bundler::FileUtils.chmod, but changes permissions recursively.
#
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
@@ -1044,15 +1842,68 @@ module Bundler::FileUtils
end
module_function :chmod_R
+ # Changes the owner and group on the entries at the paths given in +list+
+ # (a single path or an array of paths)
+ # to the given +user+ and +group+;
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
+ #
+ # - Modifies each entry that is a regular file using
+ # {File.chown}[rdoc-ref:File.chown].
+ # - Modifies each entry that is a symbolic link using
+ # {File.lchown}[rdoc-ref:File.lchown].
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
#
- # Changes owner and group on the named files (in +list+)
- # to the user +user+ and the group +group+. +user+ and +group+
- # may be an ID (Integer/String) or a name (String).
- # If +user+ or +group+ is nil, this method does not change
- # the attribute.
+ # User and group:
#
- # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
- # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
+ # - Argument +user+ may be a user name or a user id;
+ # if +nil+ or +-1+, the user is not changed.
+ # - Argument +group+ may be a group name or a group id;
+ # if +nil+ or +-1+, the group is not changed.
+ # - The user must be a member of the group.
+ #
+ # Examples:
+ #
+ # # One path.
+ # # User and group as string names.
+ # File.stat('src0.txt').uid # => 1004
+ # File.stat('src0.txt').gid # => 1004
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt')
+ # File.stat('src0.txt').uid # => 1006
+ # File.stat('src0.txt').gid # => 1005
+ #
+ # # User and group as uid and gid.
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt')
+ # File.stat('src0.txt').uid # => 1004
+ # File.stat('src0.txt').gid # => 1004
+ #
+ # # Array of paths.
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
+ #
+ # # Directory (not recursive).
+ # Bundler::FileUtils.chown('user2', 'group1', '.')
+ #
+ # Keyword arguments:
+ #
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
+ # Bundler::FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # chown user2:group1 src0.txt
+ # chown 1004:1004 src0.txt
+ # chown 1006:1005 src0.txt src0.dat
+ # chown user2:group1 src0.txt
+ # chown user2:group1 .
+ #
+ # Related: Bundler::FileUtils.chown_R.
#
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
@@ -1068,15 +1919,7 @@ module Bundler::FileUtils
end
module_function :chown
- #
- # Changes owner and group on the named files (in +list+)
- # to the user +user+ and the group +group+ recursively.
- # +user+ and +group+ may be an ID (Integer/String) or
- # a name (String). If +user+ or +group+ is nil, this
- # method does not change the attribute.
- #
- # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
- # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
+ # Like Bundler::FileUtils.chown, but changes owner and group recursively.
#
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
@@ -1127,12 +1970,50 @@ module Bundler::FileUtils
end
private_module_function :fu_get_gid
+ # Updates modification times (mtime) and access times (atime)
+ # of the entries given by the paths in +list+
+ # (a single path or an array of paths);
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
+ #
+ # By default, creates an empty file for any path to a non-existent entry;
+ # use keyword argument +nocreate+ to raise an exception instead.
+ #
+ # Argument +list+ or its elements
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
+ #
+ # Examples:
+ #
+ # # Single path.
+ # f = File.new('src0.txt') # Existing file.
+ # f.atime # => 2022-06-10 11:11:21.200277 -0700
+ # f.mtime # => 2022-06-10 11:11:21.200277 -0700
+ # Bundler::FileUtils.touch('src0.txt')
+ # f = File.new('src0.txt')
+ # f.atime # => 2022-06-11 08:28:09.8185343 -0700
+ # f.mtime # => 2022-06-11 08:28:09.8185343 -0700
#
- # Updates modification time (mtime) and access time (atime) of file(s) in
- # +list+. Files are created if they don't exist.
+ # # Array of paths.
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
#
- # Bundler::FileUtils.touch 'timestamp'
- # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
+ # Keyword arguments:
+ #
+ # - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
+ # instead of the current time.
+ # - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
+ # - <tt>noop: true</tt> - does not touch entries; returns +nil+.
+ # - <tt>verbose: true</tt> - prints an equivalent command:
+ #
+ # Bundler::FileUtils.touch('src0.txt', noop: true, verbose: true)
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
+ # Bundler::FileUtils.touch(path, noop: true, verbose: true)
+ #
+ # Output:
+ #
+ # touch src0.txt
+ # touch src0.txt src0.dat
+ # touch src0.txt
+ #
+ # Related: Bundler::FileUtils.uptodate?.
#
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
@@ -1290,14 +2171,9 @@ module Bundler::FileUtils
def entries
opts = {}
- opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
+ opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
- files = if Dir.respond_to?(:children)
- Dir.children(path, **opts)
- else
- Dir.entries(path(), **opts)
- .reject {|n| n == '.' or n == '..' }
- end
+ files = Dir.children(path, **opts)
untaint = RUBY_VERSION < '2.7'
files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
@@ -1345,6 +2221,7 @@ module Bundler::FileUtils
else
File.chmod mode, path()
end
+ rescue Errno::EOPNOTSUPP
end
def chown(uid, gid)
@@ -1439,7 +2316,7 @@ module Bundler::FileUtils
if st.symlink?
begin
File.lchmod mode, path
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
end
else
File.chmod mode, path
@@ -1498,13 +2375,21 @@ module Bundler::FileUtils
def postorder_traverse
if directory?
- entries().each do |ent|
+ begin
+ children = entries()
+ rescue Errno::EACCES
+ # Failed to get the list of children.
+ # Assuming there is no children, try to process the parent directory.
+ yield self
+ return
+ end
+
+ children.each do |ent|
ent.postorder_traverse do |e|
yield e
end
end
end
- ensure
yield self
end
@@ -1559,7 +2444,15 @@ module Bundler::FileUtils
def join(dir, base)
return File.path(dir) if not base or base == '.'
return File.path(base) if not dir or dir == '.'
- File.join(dir, base)
+ begin
+ File.join(dir, base)
+ rescue EncodingError
+ if fu_windows?
+ File.join(dir.encode(::Encoding::UTF_8), base.encode(::Encoding::UTF_8))
+ else
+ raise
+ end
+ end
end
if File::ALT_SEPARATOR
@@ -1590,15 +2483,15 @@ module Bundler::FileUtils
end
private_module_function :fu_each_src_dest
- def fu_each_src_dest0(src, dest) #:nodoc:
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
if tmp = Array.try_convert(src)
tmp.each do |s|
s = File.path(s)
- yield s, File.join(dest, File.basename(s))
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
end
else
src = File.path(src)
- if File.directory?(dest)
+ if target_directory and File.directory?(dest)
yield src, File.join(dest, File.basename(src))
else
yield src, File.path(dest)
@@ -1614,7 +2507,7 @@ module Bundler::FileUtils
def fu_output_message(msg) #:nodoc:
output = @fileutils_output if defined?(@fileutils_output)
- output ||= $stderr
+ output ||= $stdout
if defined?(@fileutils_label)
msg = @fileutils_label + msg
end
@@ -1622,6 +2515,56 @@ module Bundler::FileUtils
end
private_module_function :fu_output_message
+ def fu_split_path(path)
+ path = File.path(path)
+ list = []
+ until (parent, base = File.split(path); parent == path or parent == ".")
+ list << base
+ path = parent
+ end
+ list << path
+ list.reverse!
+ end
+ private_module_function :fu_split_path
+
+ def fu_relative_components_from(target, base) #:nodoc:
+ i = 0
+ while target[i]&.== base[i]
+ i += 1
+ end
+ Array.new(base.size-i, '..').concat(target[i..-1])
+ end
+ private_module_function :fu_relative_components_from
+
+ def fu_clean_components(*comp)
+ comp.shift while comp.first == "."
+ return comp if comp.empty?
+ clean = [comp.shift]
+ path = File.join(*clean, "") # ending with File::SEPARATOR
+ while c = comp.shift
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
+ clean.pop
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ else
+ clean << c
+ path << c << "/"
+ end
+ end
+ clean
+ end
+ private_module_function :fu_clean_components
+
+ if fu_windows?
+ def fu_starting_path?(path)
+ path&.start_with?(%r(\w:|/))
+ end
+ else
+ def fu_starting_path?(path)
+ path&.start_with?("/")
+ end
+ end
+ private_module_function :fu_starting_path?
+
# This hash table holds command options.
OPT_TABLE = {} #:nodoc: internal use only
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
@@ -1631,50 +2574,49 @@ module Bundler::FileUtils
public
+ # Returns an array of the string names of \Bundler::FileUtils methods
+ # that accept one or more keyword arguments:
#
- # Returns an Array of names of high-level methods that accept any keyword
- # arguments.
- #
- # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
+ # Bundler::FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
#
def self.commands
OPT_TABLE.keys
end
+ # Returns an array of the string keyword names:
#
- # Returns an Array of option names.
- #
- # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
+ # Bundler::FileUtils.options.take(3) # => ["noop", "verbose", "force"]
#
def self.options
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
end
+ # Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
+ # the arguments may be strings or symbols:
#
- # Returns true if the method +mid+ have an option +opt+.
- #
- # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
- # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
- # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
+ # Bundler::FileUtils.have_option?(:chmod, :noop) # => true
+ # Bundler::FileUtils.have_option?('chmod', 'secure') # => false
#
def self.have_option?(mid, opt)
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
li.include?(opt)
end
+ # Returns an array of the string keyword name for method +mid+;
+ # the argument may be a string or a symbol:
#
- # Returns an Array of option names of the method +mid+.
- #
- # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
+ # Bundler::FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
+ # Bundler::FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
#
def self.options_of(mid)
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
end
+ # Returns an array of the string method names of the methods
+ # that accept the given keyword option +opt+;
+ # the argument must be a symbol:
#
- # Returns an Array of methods names which have the option +opt+.
- #
- # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+ # Bundler::FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
#
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb
deleted file mode 100644
index a52b96deaf..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'molinillo/gem_metadata'
-require_relative 'molinillo/errors'
-require_relative 'molinillo/resolver'
-require_relative 'molinillo/modules/ui'
-require_relative 'molinillo/modules/specification_provider'
-
-# Bundler::Molinillo is a generic dependency resolution algorithm.
-module Bundler::Molinillo
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
deleted file mode 100644
index bcacf35243..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # @!visibility private
- module Delegates
- # Delegates all {Bundler::Molinillo::ResolutionState} methods to a `#state` property.
- module ResolutionState
- # (see Bundler::Molinillo::ResolutionState#name)
- def name
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.name
- end
-
- # (see Bundler::Molinillo::ResolutionState#requirements)
- def requirements
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.requirements
- end
-
- # (see Bundler::Molinillo::ResolutionState#activated)
- def activated
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.activated
- end
-
- # (see Bundler::Molinillo::ResolutionState#requirement)
- def requirement
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.requirement
- end
-
- # (see Bundler::Molinillo::ResolutionState#possibilities)
- def possibilities
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.possibilities
- end
-
- # (see Bundler::Molinillo::ResolutionState#depth)
- def depth
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.depth
- end
-
- # (see Bundler::Molinillo::ResolutionState#conflicts)
- def conflicts
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.conflicts
- end
-
- # (see Bundler::Molinillo::ResolutionState#unused_unwind_options)
- def unused_unwind_options
- current_state = state || Bundler::Molinillo::ResolutionState.empty
- current_state.unused_unwind_options
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
deleted file mode 100644
index f8c695c1ed..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- module Delegates
- # Delegates all {Bundler::Molinillo::SpecificationProvider} methods to a
- # `#specification_provider` property.
- module SpecificationProvider
- # (see Bundler::Molinillo::SpecificationProvider#search_for)
- def search_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.search_for(dependency)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#dependencies_for)
- def dependencies_for(specification)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_for(specification)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#requirement_satisfied_by?)
- def requirement_satisfied_by?(requirement, activated, spec)
- with_no_such_dependency_error_handling do
- specification_provider.requirement_satisfied_by?(requirement, activated, spec)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#dependencies_equal?)
- def dependencies_equal?(dependencies, other_dependencies)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_equal?(dependencies, other_dependencies)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for)
- def name_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.name_for(dependency)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
- def name_for_explicit_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_explicit_dependency_source
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#name_for_locking_dependency_source)
- def name_for_locking_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_locking_dependency_source
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#sort_dependencies)
- def sort_dependencies(dependencies, activated, conflicts)
- with_no_such_dependency_error_handling do
- specification_provider.sort_dependencies(dependencies, activated, conflicts)
- end
- end
-
- # (see Bundler::Molinillo::SpecificationProvider#allow_missing?)
- def allow_missing?(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.allow_missing?(dependency)
- end
- end
-
- private
-
- # Ensures any raised {NoSuchDependencyError} has its
- # {NoSuchDependencyError#required_by} set.
- # @yield
- def with_no_such_dependency_error_handling
- yield
- rescue NoSuchDependencyError => error
- if state
- vertex = activated.vertex_named(name_for(error.dependency))
- error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
- error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
- end
- raise
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
deleted file mode 100644
index 4d577213b9..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../vendored_tsort'
-
-require_relative 'dependency_graph/log'
-require_relative 'dependency_graph/vertex'
-
-module Bundler::Molinillo
- # A directed acyclic graph that is tuned to hold named dependencies
- class DependencyGraph
- include Enumerable
-
- # Enumerates through the vertices of the graph.
- # @return [Array<Vertex>] The graph's vertices.
- def each
- return vertices.values.each unless block_given?
- vertices.values.each { |v| yield v }
- end
-
- include Bundler::TSort
-
- # @!visibility private
- alias tsort_each_node each
-
- # @!visibility private
- def tsort_each_child(vertex, &block)
- vertex.successors.each(&block)
- end
-
- # Topologically sorts the given vertices.
- # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must
- # all belong to the same graph.
- # @return [Array<Vertex>] The sorted vertices.
- def self.tsort(vertices)
- Bundler::TSort.tsort(
- lambda { |b| vertices.each(&b) },
- lambda { |v, &b| (v.successors & vertices).each(&b) }
- )
- end
-
- # A directed edge of a {DependencyGraph}
- # @attr [Vertex] origin The origin of the directed edge
- # @attr [Vertex] destination The destination of the directed edge
- # @attr [Object] requirement The requirement the directed edge represents
- Edge = Struct.new(:origin, :destination, :requirement)
-
- # @return [{String => Vertex}] the vertices of the dependency graph, keyed
- # by {Vertex#name}
- attr_reader :vertices
-
- # @return [Log] the op log for this graph
- attr_reader :log
-
- # Initializes an empty dependency graph
- def initialize
- @vertices = {}
- @log = Log.new
- end
-
- # Tags the current state of the dependency as the given tag
- # @param [Object] tag an opaque tag for the current state of the graph
- # @return [Void]
- def tag(tag)
- log.tag(self, tag)
- end
-
- # Rewinds the graph to the state tagged as `tag`
- # @param [Object] tag the tag to rewind to
- # @return [Void]
- def rewind_to(tag)
- log.rewind_to(self, tag)
- end
-
- # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
- # are properly copied.
- # @param [DependencyGraph] other the graph to copy.
- def initialize_copy(other)
- super
- @vertices = {}
- @log = other.log.dup
- traverse = lambda do |new_v, old_v|
- return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
- old_v.outgoing_edges.each do |edge|
- destination = add_vertex(edge.destination.name, edge.destination.payload)
- add_edge_no_circular(new_v, destination, edge.requirement)
- traverse.call(destination, edge.destination)
- end
- end
- other.vertices.each do |name, vertex|
- new_vertex = add_vertex(name, vertex.payload, vertex.root?)
- new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
- traverse.call(new_vertex, vertex)
- end
- end
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{vertices.values.inspect}"
- end
-
- # @param [Hash] options options for dot output.
- # @return [String] Returns a dot format representation of the graph
- def to_dot(options = {})
- edge_label = options.delete(:edge_label)
- raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
-
- dot_vertices = []
- dot_edges = []
- vertices.each do |n, v|
- dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
- v.outgoing_edges.each do |e|
- label = edge_label ? edge_label.call(e) : e.requirement
- dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
- end
- end
-
- dot_vertices.uniq!
- dot_vertices.sort!
- dot_edges.uniq!
- dot_edges.sort!
-
- dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
- dot.join("\n")
- end
-
- # @param [DependencyGraph] other
- # @return [Boolean] whether the two dependency graphs are equal, determined
- # by a recursive traversal of each {#root_vertices} and its
- # {Vertex#successors}
- def ==(other)
- return false unless other
- return true if equal?(other)
- vertices.each do |name, vertex|
- other_vertex = other.vertex_named(name)
- return false unless other_vertex
- return false unless vertex.payload == other_vertex.payload
- return false unless other_vertex.successors.to_set == vertex.successors.to_set
- end
- end
-
- # @param [String] name
- # @param [Object] payload
- # @param [Array<String>] parent_names
- # @param [Object] requirement the requirement that is requiring the child
- # @return [void]
- def add_child_vertex(name, payload, parent_names, requirement)
- root = !parent_names.delete(nil) { true }
- vertex = add_vertex(name, payload, root)
- vertex.explicit_requirements << requirement if root
- parent_names.each do |parent_name|
- parent_vertex = vertex_named(parent_name)
- add_edge(parent_vertex, vertex, requirement)
- end
- vertex
- end
-
- # Adds a vertex with the given name, or updates the existing one.
- # @param [String] name
- # @param [Object] payload
- # @return [Vertex] the vertex that was added to `self`
- def add_vertex(name, payload, root = false)
- log.add_vertex(self, name, payload, root)
- end
-
- # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
- # removing any non-root vertices that were orphaned in the process
- # @param [String] name
- # @return [Array<Vertex>] the vertices which have been detached
- def detach_vertex_named(name)
- log.detach_vertex_named(self, name)
- end
-
- # @param [String] name
- # @return [Vertex,nil] the vertex with the given name
- def vertex_named(name)
- vertices[name]
- end
-
- # @param [String] name
- # @return [Vertex,nil] the root vertex with the given name
- def root_vertex_named(name)
- vertex = vertex_named(name)
- vertex if vertex && vertex.root?
- end
-
- # Adds a new {Edge} to the dependency graph
- # @param [Vertex] origin
- # @param [Vertex] destination
- # @param [Object] requirement the requirement that this edge represents
- # @return [Edge] the added edge
- def add_edge(origin, destination, requirement)
- if destination.path_to?(origin)
- raise CircularDependencyError.new(path(destination, origin))
- end
- add_edge_no_circular(origin, destination, requirement)
- end
-
- # Deletes an {Edge} from the dependency graph
- # @param [Edge] edge
- # @return [Void]
- def delete_edge(edge)
- log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
- end
-
- # Sets the payload of the vertex with the given name
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload
- # @return [Void]
- def set_payload(name, payload)
- log.set_payload(self, name, payload)
- end
-
- private
-
- # Adds a new {Edge} to the dependency graph without checking for
- # circularity.
- # @param (see #add_edge)
- # @return (see #add_edge)
- def add_edge_no_circular(origin, destination, requirement)
- log.add_edge_no_circular(self, origin.name, destination.name, requirement)
- end
-
- # Returns the path between two vertices
- # @raise [ArgumentError] if there is no path between the vertices
- # @param [Vertex] from
- # @param [Vertex] to
- # @return [Array<Vertex>] the shortest path from `from` to `to`
- def path(from, to)
- distances = Hash.new(vertices.size + 1)
- distances[from.name] = 0
- predecessors = {}
- each do |vertex|
- vertex.successors.each do |successor|
- if distances[successor.name] > distances[vertex.name] + 1
- distances[successor.name] = distances[vertex.name] + 1
- predecessors[successor] = vertex
- end
- end
- end
-
- path = [to]
- while before = predecessors[to]
- path << before
- to = before
- break if to == from
- end
-
- unless path.last.equal?(from)
- raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
- end
-
- path.reverse
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
deleted file mode 100644
index c04c7eec9c..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class DependencyGraph
- # An action that modifies a {DependencyGraph} that is reversible.
- # @abstract
- class Action
- # rubocop:disable Lint/UnusedMethodArgument
-
- # @return [Symbol] The name of the action.
- def self.action_name
- raise 'Abstract'
- end
-
- # Performs the action on the given graph.
- # @param [DependencyGraph] graph the graph to perform the action on.
- # @return [Void]
- def up(graph)
- raise 'Abstract'
- end
-
- # Reverses the action on the given graph.
- # @param [DependencyGraph] graph the graph to reverse the action on.
- # @return [Void]
- def down(graph)
- raise 'Abstract'
- end
-
- # @return [Action,Nil] The previous action
- attr_accessor :previous
-
- # @return [Action,Nil] The next action
- attr_accessor :next
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
deleted file mode 100644
index 946a08236e..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_edge_no_circular)
- class AddEdgeNoCircular < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- delete_first(edge.origin.outgoing_edges, edge)
- delete_first(edge.destination.incoming_edges, edge)
- end
-
- # @!group AddEdgeNoCircular
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement)
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin the name of the origin of the edge
- # @param [String] destination the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin, destination, requirement)
- @origin = origin
- @destination = destination
- @requirement = requirement
- end
-
- private
-
- def delete_first(array, item)
- return unless index = array.index(item)
- array.delete_at(index)
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
deleted file mode 100644
index 483527daf8..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_vertex)
- class AddVertex < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- if existing = graph.vertices[name]
- @existing_payload = existing.payload
- @existing_root = existing.root
- end
- vertex = existing || Vertex.new(name, payload)
- graph.vertices[vertex.name] = vertex
- vertex.payload ||= payload
- vertex.root ||= root
- vertex
- end
-
- # (see Action#down)
- def down(graph)
- if defined?(@existing_payload)
- vertex = graph.vertices[name]
- vertex.payload = @existing_payload
- vertex.root = @existing_root
- else
- graph.vertices.delete(name)
- end
- end
-
- # @!group AddVertex
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # @return [Boolean] whether the vertex is root or not
- attr_reader :root
-
- # Initialize an action to add a vertex to a dependency graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- # @param [Boolean] root whether the vertex is root or not
- def initialize(name, payload, root)
- @name = name
- @payload = payload
- @root = root
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
deleted file mode 100644
index d81940585a..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#delete_edge)
- class DeleteEdge < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :delete_edge
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges.delete(edge)
- edge.destination.incoming_edges.delete(edge)
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # @!group DeleteEdge
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin_name
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination_name
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(
- graph.vertex_named(origin_name),
- graph.vertex_named(destination_name),
- requirement
- )
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin_name the name of the origin of the edge
- # @param [String] destination_name the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin_name, destination_name, requirement)
- @origin_name = origin_name
- @destination_name = destination_name
- @requirement = requirement
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
deleted file mode 100644
index 36fce7c526..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#detach_vertex_named
- class DetachVertexNamed < Action
- # @!group Action
-
- # (see Action#name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- return [] unless @vertex = graph.vertices.delete(name)
-
- removed_vertices = [@vertex]
- @vertex.outgoing_edges.each do |e|
- v = e.destination
- v.incoming_edges.delete(e)
- if !v.root? && v.incoming_edges.empty?
- removed_vertices.concat graph.detach_vertex_named(v.name)
- end
- end
-
- @vertex.incoming_edges.each do |e|
- v = e.origin
- v.outgoing_edges.delete(e)
- end
-
- removed_vertices
- end
-
- # (see Action#down)
- def down(graph)
- return unless @vertex
- graph.vertices[@vertex.name] = @vertex
- @vertex.outgoing_edges.each do |e|
- e.destination.incoming_edges << e
- end
- @vertex.incoming_edges.each do |e|
- e.origin.outgoing_edges << e
- end
- end
-
- # @!group DetachVertexNamed
-
- # @return [String] the name of the vertex to detach
- attr_reader :name
-
- # Initialize an action to detach a vertex from a dependency graph
- # @param [String] name the name of the vertex to detach
- def initialize(name)
- @name = name
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
deleted file mode 100644
index 6f0de19886..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'add_edge_no_circular'
-require_relative 'add_vertex'
-require_relative 'delete_edge'
-require_relative 'detach_vertex_named'
-require_relative 'set_payload'
-require_relative 'tag'
-
-module Bundler::Molinillo
- class DependencyGraph
- # A log for dependency graph actions
- class Log
- # Initializes an empty log
- def initialize
- @current_action = @first_action = nil
- end
-
- # @!macro [new] action
- # {include:DependencyGraph#$0}
- # @param [Graph] graph the graph to perform the action on
- # @param (see DependencyGraph#$0)
- # @return (see DependencyGraph#$0)
-
- # @macro action
- def tag(graph, tag)
- push_action(graph, Tag.new(tag))
- end
-
- # @macro action
- def add_vertex(graph, name, payload, root)
- push_action(graph, AddVertex.new(name, payload, root))
- end
-
- # @macro action
- def detach_vertex_named(graph, name)
- push_action(graph, DetachVertexNamed.new(name))
- end
-
- # @macro action
- def add_edge_no_circular(graph, origin, destination, requirement)
- push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
- end
-
- # {include:DependencyGraph#delete_edge}
- # @param [Graph] graph the graph to perform the action on
- # @param [String] origin_name
- # @param [String] destination_name
- # @param [Object] requirement
- # @return (see DependencyGraph#delete_edge)
- def delete_edge(graph, origin_name, destination_name, requirement)
- push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement))
- end
-
- # @macro action
- def set_payload(graph, name, payload)
- push_action(graph, SetPayload.new(name, payload))
- end
-
- # Pops the most recent action from the log and undoes the action
- # @param [DependencyGraph] graph
- # @return [Action] the action that was popped off the log
- def pop!(graph)
- return unless action = @current_action
- unless @current_action = action.previous
- @first_action = nil
- end
- action.down(graph)
- action
- end
-
- extend Enumerable
-
- # @!visibility private
- # Enumerates each action in the log
- # @yield [Action]
- def each
- return enum_for unless block_given?
- action = @first_action
- loop do
- break unless action
- yield action
- action = action.next
- end
- self
- end
-
- # @!visibility private
- # Enumerates each action in the log in reverse order
- # @yield [Action]
- def reverse_each
- return enum_for(:reverse_each) unless block_given?
- action = @current_action
- loop do
- break unless action
- yield action
- action = action.previous
- end
- self
- end
-
- # @macro action
- def rewind_to(graph, tag)
- loop do
- action = pop!(graph)
- raise "No tag #{tag.inspect} found" unless action
- break if action.class.action_name == :tag && action.tag == tag
- end
- end
-
- private
-
- # Adds the given action to the log, running the action
- # @param [DependencyGraph] graph
- # @param [Action] action
- # @return The value returned by `action.up`
- def push_action(graph, action)
- action.previous = @current_action
- @current_action.next = action if @current_action
- @current_action = action
- @first_action ||= action
- action.up(graph)
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
deleted file mode 100644
index 2e9b90e6cd..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#set_payload
- class SetPayload < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :set_payload
- end
-
- # (see Action#up)
- def up(graph)
- vertex = graph.vertex_named(name)
- @old_payload = vertex.payload
- vertex.payload = payload
- end
-
- # (see Action#down)
- def down(graph)
- graph.vertex_named(name).payload = @old_payload
- end
-
- # @!group SetPayload
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # Initialize an action to add set the payload for a vertex in a dependency
- # graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- def initialize(name, payload)
- @name = name
- @payload = payload
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
deleted file mode 100644
index 5b5da3e4f9..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Bundler::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#tag
- class Tag < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :tag
- end
-
- # (see Action#up)
- def up(graph)
- end
-
- # (see Action#down)
- def down(graph)
- end
-
- # @!group Tag
-
- # @return [Object] An opaque tag
- attr_reader :tag
-
- # Initialize an action to tag a state of a dependency graph
- # @param [Object] tag an opaque tag
- def initialize(tag)
- @tag = tag
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
deleted file mode 100644
index 1185a8ab05..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class DependencyGraph
- # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
- # {#payload}
- class Vertex
- # @return [String] the name of the vertex
- attr_accessor :name
-
- # @return [Object] the payload the vertex holds
- attr_accessor :payload
-
- # @return [Array<Object>] the explicit requirements that required
- # this vertex
- attr_reader :explicit_requirements
-
- # @return [Boolean] whether the vertex is considered a root vertex
- attr_accessor :root
- alias root? root
-
- # Initializes a vertex with the given name and payload.
- # @param [String] name see {#name}
- # @param [Object] payload see {#payload}
- def initialize(name, payload)
- @name = name.frozen? ? name : name.dup.freeze
- @payload = payload
- @explicit_requirements = []
- @outgoing_edges = []
- @incoming_edges = []
- end
-
- # @return [Array<Object>] all of the requirements that required
- # this vertex
- def requirements
- (incoming_edges.map(&:requirement) + explicit_requirements).uniq
- end
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#origin}
- attr_accessor :outgoing_edges
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#destination}
- attr_accessor :incoming_edges
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#destination}
- def predecessors
- incoming_edges.map(&:origin)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def recursive_predecessors
- _recursive_predecessors
- end
-
- # @param [Set<Vertex>] vertices the set to add the predecessors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def _recursive_predecessors(vertices = new_vertex_set)
- incoming_edges.each do |edge|
- vertex = edge.origin
- next unless vertices.add?(vertex)
- vertex._recursive_predecessors(vertices)
- end
-
- vertices
- end
- protected :_recursive_predecessors
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#origin}
- def successors
- outgoing_edges.map(&:destination)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def recursive_successors
- _recursive_successors
- end
-
- # @param [Set<Vertex>] vertices the set to add the successors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def _recursive_successors(vertices = new_vertex_set)
- outgoing_edges.each do |edge|
- vertex = edge.destination
- next unless vertices.add?(vertex)
- vertex._recursive_successors(vertices)
- end
-
- vertices
- end
- protected :_recursive_successors
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{name}(#{payload.inspect})"
- end
-
- # @return [Boolean] whether the two vertices are equal, determined
- # by a recursive traversal of each {Vertex#successors}
- def ==(other)
- return true if equal?(other)
- shallow_eql?(other) &&
- successors.to_set == other.successors.to_set
- end
-
- # @param [Vertex] other the other vertex to compare to
- # @return [Boolean] whether the two vertices are equal, determined
- # solely by {#name} and {#payload} equality
- def shallow_eql?(other)
- return true if equal?(other)
- other &&
- name == other.name &&
- payload == other.payload
- end
-
- alias eql? ==
-
- # @return [Fixnum] a hash for the vertex based upon its {#name}
- def hash
- name.hash
- end
-
- # Is there a path from `self` to `other` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def path_to?(other)
- _path_to?(other)
- end
-
- alias descendent? path_to?
-
- # @param [Vertex] other the vertex to check if there's a path to
- # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
- # @return [Boolean] whether there is a path to `other` from `self`
- def _path_to?(other, visited = new_vertex_set)
- return false unless visited.add?(self)
- return true if equal?(other)
- successors.any? { |v| v._path_to?(other, visited) }
- end
- protected :_path_to?
-
- # Is there a path from `other` to `self` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def ancestor?(other)
- other.path_to?(self)
- end
-
- alias is_reachable_from? ancestor?
-
- def new_vertex_set
- require 'set'
- Set.new
- end
- private :new_vertex_set
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
deleted file mode 100644
index 8c8cafb447..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
+++ /dev/null
@@ -1,149 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # An error that occurred during the resolution process
- class ResolverError < StandardError; end
-
- # An error caused by searching for a dependency that is completely unknown,
- # i.e. has no versions available whatsoever.
- class NoSuchDependencyError < ResolverError
- # @return [Object] the dependency that could not be found
- attr_accessor :dependency
-
- # @return [Array<Object>] the specifications that depended upon {#dependency}
- attr_accessor :required_by
-
- # Initializes a new error with the given missing dependency.
- # @param [Object] dependency @see {#dependency}
- # @param [Array<Object>] required_by @see {#required_by}
- def initialize(dependency, required_by = [])
- @dependency = dependency
- @required_by = required_by.uniq
- super()
- end
-
- # The error message for the missing dependency, including the specifications
- # that had this dependency.
- def message
- sources = required_by.map { |r| "`#{r}`" }.join(' and ')
- message = "Unable to find a specification for `#{dependency}`"
- message += " depended upon by #{sources}" unless sources.empty?
- message
- end
- end
-
- # An error caused by attempting to fulfil a dependency that was circular
- #
- # @note This exception will be thrown if and only if a {Vertex} is added to a
- # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
- # existing {DependencyGraph::Vertex}
- class CircularDependencyError < ResolverError
- # [Set<Object>] the dependencies responsible for causing the error
- attr_reader :dependencies
-
- # Initializes a new error with the given circular vertices.
- # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
- # that caused the error
- def initialize(vertices)
- super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
- @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
- end
- end
-
- # An error caused by conflicts in version
- class VersionConflict < ResolverError
- # @return [{String => Resolution::Conflict}] the conflicts that caused
- # resolution to fail
- attr_reader :conflicts
-
- # @return [SpecificationProvider] the specification provider used during
- # resolution
- attr_reader :specification_provider
-
- # Initializes a new error with the given version conflicts.
- # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
- # @param [SpecificationProvider] specification_provider see {#specification_provider}
- def initialize(conflicts, specification_provider)
- pairs = []
- conflicts.values.flat_map(&:requirements).each do |conflicting|
- conflicting.each do |source, conflict_requirements|
- conflict_requirements.each do |c|
- pairs << [c, source]
- end
- end
- end
-
- super "Unable to satisfy the following requirements:\n\n" \
- "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
-
- @conflicts = conflicts
- @specification_provider = specification_provider
- end
-
- require_relative 'delegates/specification_provider'
- include Delegates::SpecificationProvider
-
- # @return [String] An error message that includes requirement trees,
- # which is much more detailed & customizable than the default message
- # @param [Hash] opts the options to create a message with.
- # @option opts [String] :solver_name The user-facing name of the solver
- # @option opts [String] :possibility_type The generic name of a possibility
- # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
- # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
- # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
- # messages for each conflict
- # @option opts [Proc] :version_for_spec A proc that returns the version number for a
- # possibility
- def message_with_trees(opts = {})
- solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
- possibility_type = opts.delete(:possibility_type) { 'possibility named' }
- reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
- printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
- additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
- version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
- incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
- proc do |name, _conflict|
- %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
- end
- end
-
- full_message_for_conflict = opts.delete(:full_message_for_conflict) do
- proc do |name, conflict|
- o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = reduce_trees.call(conflict.requirement_trees)
-
- o << trees.map do |tree|
- t = ''.dup
- depth = 2
- tree.each do |req|
- t << ' ' * depth << printable_requirement.call(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[name_for(req)]
- t << %( was resolved to #{version_for_spec.call(spec)}, which)
- end
- t << %( depends on)
- end
- t << %(\n)
- depth += 1
- end
- t
- end.join("\n")
-
- additional_message_for_conflict.call(o, name, conflict)
-
- o
- end
- end
-
- conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
- o << full_message_for_conflict.call(name, conflict)
- end.strip
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
deleted file mode 100644
index a0cfc21672..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # The version of Bundler::Molinillo.
- VERSION = '0.8.0'.freeze
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
deleted file mode 100644
index eeae79af3c..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # Provides information about specifications and dependencies to the resolver,
- # allowing the {Resolver} class to remain generic while still providing power
- # and flexibility.
- #
- # This module contains the methods that users of Bundler::Molinillo must to implement,
- # using knowledge of their own model classes.
- module SpecificationProvider
- # Search for the specifications that match the given dependency.
- # The specifications in the returned array will be considered in reverse
- # order, so the latest version ought to be last.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [Array<Object>] the specifications that satisfy the given
- # `dependency`.
- def search_for(dependency)
- []
- end
-
- # Returns the dependencies of `specification`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `specification` parameter.
- #
- # @param [Object] specification
- # @return [Array<Object>] the dependencies that are required by the given
- # `specification`.
- def dependencies_for(specification)
- []
- end
-
- # Determines whether the given `requirement` is satisfied by the given
- # `spec`, in the context of the current `activated` dependency graph.
- #
- # @param [Object] requirement
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [Object] spec
- # @return [Boolean] whether `requirement` is satisfied by `spec` in the
- # context of the current `activated` dependency graph.
- def requirement_satisfied_by?(requirement, activated, spec)
- true
- end
-
- # Determines whether two arrays of dependencies are equal, and thus can be
- # grouped.
- #
- # @param [Array<Object>] dependencies
- # @param [Array<Object>] other_dependencies
- # @return [Boolean] whether `dependencies` and `other_dependencies` should
- # be considered equal.
- def dependencies_equal?(dependencies, other_dependencies)
- dependencies == other_dependencies
- end
-
- # Returns the name for the given `dependency`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [String] the name for the given `dependency`.
- def name_for(dependency)
- dependency.to_s
- end
-
- # @return [String] the name of the source of explicit dependencies, i.e.
- # those passed to {Resolver#resolve} directly.
- def name_for_explicit_dependency_source
- 'user-specified dependency'
- end
-
- # @return [String] the name of the source of 'locked' dependencies, i.e.
- # those passed to {Resolver#resolve} directly as the `base`
- def name_for_locking_dependency_source
- 'Lockfile'
- end
-
- # Sort dependencies so that the ones that are easiest to resolve are first.
- # Easiest to resolve is (usually) defined by:
- # 1) Is this dependency already activated?
- # 2) How relaxed are the requirements?
- # 3) Are there any conflicts for this dependency?
- # 4) How many possibilities are there to satisfy this dependency?
- #
- # @param [Array<Object>] dependencies
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [{String => Array<Conflict>}] conflicts
- # @return [Array<Object>] a sorted copy of `dependencies`.
- def sort_dependencies(dependencies, activated, conflicts)
- dependencies.sort_by do |dependency|
- name = name_for(dependency)
- [
- activated.vertex_named(name).payload ? 0 : 1,
- conflicts[name] ? 0 : 1,
- ]
- end
- end
-
- # Returns whether this dependency, which has no possible matching
- # specifications, can safely be ignored.
- #
- # @param [Object] dependency
- # @return [Boolean] whether this dependency can safely be skipped.
- def allow_missing?(dependency)
- false
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
deleted file mode 100644
index a166bc6991..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # Conveys information about the resolution process to a user.
- module UI
- # The {IO} object that should be used to print output. `STDOUT`, by default.
- #
- # @return [IO]
- def output
- STDOUT
- end
-
- # Called roughly every {#progress_rate}, this method should convey progress
- # to the user.
- #
- # @return [void]
- def indicate_progress
- output.print '.' unless debug?
- end
-
- # How often progress should be conveyed to the user via
- # {#indicate_progress}, in seconds. A third of a second, by default.
- #
- # @return [Float]
- def progress_rate
- 0.33
- end
-
- # Called before resolution begins.
- #
- # @return [void]
- def before_resolution
- output.print 'Resolving dependencies...'
- end
-
- # Called after resolution ends (either successfully or with an error).
- # By default, prints a newline.
- #
- # @return [void]
- def after_resolution
- output.puts
- end
-
- # Conveys debug information to the user.
- #
- # @param [Integer] depth the current depth of the resolution process.
- # @return [void]
- def debug(depth = 0)
- if debug?
- debug_info = yield
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
- debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
- output.puts debug_info
- end
- end
-
- # Whether or not debug messages should be printed.
- # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
- # set.
- #
- # @return [Boolean]
- def debug?
- return @debug_mode if defined?(@debug_mode)
- @debug_mode = ENV['MOLINILLO_DEBUG']
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
deleted file mode 100644
index c689ca7635..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
+++ /dev/null
@@ -1,839 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- class Resolver
- # A specific resolution from a given {Resolver}
- class Resolution
- # A conflict that the resolution process encountered
- # @attr [Object] requirement the requirement that immediately led to the conflict
- # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
- # @attr [Object, nil] existing the existing spec that was in conflict with
- # the {#possibility}
- # @attr [Object] possibility_set the set of specs that was unable to be
- # activated due to a conflict.
- # @attr [Object] locked_requirement the relevant locking requirement.
- # @attr [Array<Array<Object>>] requirement_trees the different requirement
- # trees that led to every requirement for the conflicting name.
- # @attr [{String=>Object}] activated_by_name the already-activated specs.
- # @attr [Object] underlying_error an error that has occurred during resolution, and
- # will be raised at the end of it if no resolution is found.
- Conflict = Struct.new(
- :requirement,
- :requirements,
- :existing,
- :possibility_set,
- :locked_requirement,
- :requirement_trees,
- :activated_by_name,
- :underlying_error
- )
-
- class Conflict
- # @return [Object] a spec that was unable to be activated due to a conflict
- def possibility
- possibility_set && possibility_set.latest_version
- end
- end
-
- # A collection of possibility states that share the same dependencies
- # @attr [Array] dependencies the dependencies for this set of possibilities
- # @attr [Array] possibilities the possibilities
- PossibilitySet = Struct.new(:dependencies, :possibilities)
-
- class PossibilitySet
- # String representation of the possibility set, for debugging
- def to_s
- "[#{possibilities.join(', ')}]"
- end
-
- # @return [Object] most up-to-date dependency in the possibility set
- def latest_version
- possibilities.last
- end
- end
-
- # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
- # @attr [Integer] state_index the index of the state to unwind to
- # @attr [Object] state_requirement the requirement of the state we're unwinding to
- # @attr [Array] requirement_tree for the requirement we're relaxing
- # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
- # @attr [Array] requirement_trees for the conflict
- # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
- UnwindDetails = Struct.new(
- :state_index,
- :state_requirement,
- :requirement_tree,
- :conflicting_requirements,
- :requirement_trees,
- :requirements_unwound_to_instead
- )
-
- class UnwindDetails
- include Comparable
-
- # We compare UnwindDetails when choosing which state to unwind to. If
- # two options have the same state_index we prefer the one most
- # removed from a requirement that caused the conflict. Both options
- # would unwind to the same state, but a `grandparent` option will
- # filter out fewer of its possibilities after doing so - where a state
- # is both a `parent` and a `grandparent` to requirements that have
- # caused a conflict this is the correct behaviour.
- # @param [UnwindDetail] other UnwindDetail to be compared
- # @return [Integer] integer specifying ordering
- def <=>(other)
- if state_index > other.state_index
- 1
- elsif state_index == other.state_index
- reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
- else
- -1
- end
- end
-
- # @return [Integer] index of state requirement in reversed requirement tree
- # (the conflicting requirement itself will be at position 0)
- def reversed_requirement_tree_index
- @reversed_requirement_tree_index ||=
- if state_requirement
- requirement_tree.reverse.index(state_requirement)
- else
- 999_999
- end
- end
-
- # @return [Boolean] where the requirement of the state we're unwinding
- # to directly caused the conflict. Note: in this case, it is
- # impossible for the state we're unwinding to to be a parent of
- # any of the other conflicting requirements (or we would have
- # circularity)
- def unwinding_to_primary_requirement?
- requirement_tree.last == state_requirement
- end
-
- # @return [Array] array of sub-dependencies to avoid when choosing a
- # new possibility for the state we've unwound to. Only relevant for
- # non-primary unwinds
- def sub_dependencies_to_avoid
- @requirements_to_avoid ||=
- requirement_trees.map do |tree|
- index = tree.index(state_requirement)
- tree[index + 1] if index
- end.compact
- end
-
- # @return [Array] array of all the requirements that led to the need for
- # this unwind
- def all_requirements
- @all_requirements ||= requirement_trees.flatten(1)
- end
- end
-
- # @return [SpecificationProvider] the provider that knows about
- # dependencies, requirements, specifications, versions, etc.
- attr_reader :specification_provider
-
- # @return [UI] the UI that knows how to communicate feedback about the
- # resolution process back to the user
- attr_reader :resolver_ui
-
- # @return [DependencyGraph] the base dependency graph to which
- # dependencies should be 'locked'
- attr_reader :base
-
- # @return [Array] the dependencies that were explicitly required
- attr_reader :original_requested
-
- # Initializes a new resolution.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui see {#resolver_ui}
- # @param [Array] requested see {#original_requested}
- # @param [DependencyGraph] base see {#base}
- def initialize(specification_provider, resolver_ui, requested, base)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- @original_requested = requested
- @base = base
- @states = []
- @iteration_counter = 0
- @parents_of = Hash.new { |h, k| h[k] = [] }
- end
-
- # Resolves the {#original_requested} dependencies into a full dependency
- # graph
- # @raise [ResolverError] if successful resolution is impossible
- # @return [DependencyGraph] the dependency graph of successfully resolved
- # dependencies
- def resolve
- start_resolution
-
- while state
- break if !state.requirement && state.requirements.empty?
- indicate_progress
- if state.respond_to?(:pop_possibility_state) # DependencyState
- debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
- state.pop_possibility_state.tap do |s|
- if s
- states.push(s)
- activated.tag(s)
- end
- end
- end
- process_topmost_state
- end
-
- resolve_activated_specs
- ensure
- end_resolution
- end
-
- # @return [Integer] the number of resolver iterations in between calls to
- # {#resolver_ui}'s {UI#indicate_progress} method
- attr_accessor :iteration_rate
- private :iteration_rate
-
- # @return [Time] the time at which resolution began
- attr_accessor :started_at
- private :started_at
-
- # @return [Array<ResolutionState>] the stack of states for the resolution
- attr_accessor :states
- private :states
-
- private
-
- # Sets up the resolution process
- # @return [void]
- def start_resolution
- @started_at = Time.now
-
- push_initial_state
-
- debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
- resolver_ui.before_resolution
- end
-
- def resolve_activated_specs
- activated.vertices.each do |_, vertex|
- next unless vertex.payload
-
- latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
- vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
- end
-
- activated.set_payload(vertex.name, latest_version)
- end
- activated.freeze
- end
-
- # Ends the resolution process
- # @return [void]
- def end_resolution
- resolver_ui.after_resolution
- debug do
- "Finished resolution (#{@iteration_counter} steps) " \
- "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
- end
- debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
- debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
- end
-
- require_relative 'state'
- require_relative 'modules/specification_provider'
-
- require_relative 'delegates/resolution_state'
- require_relative 'delegates/specification_provider'
-
- include Bundler::Molinillo::Delegates::ResolutionState
- include Bundler::Molinillo::Delegates::SpecificationProvider
-
- # Processes the topmost available {RequirementState} on the stack
- # @return [void]
- def process_topmost_state
- if possibility
- attempt_to_activate
- else
- create_conflict
- unwind_for_conflict
- end
- rescue CircularDependencyError => underlying_error
- create_conflict(underlying_error)
- unwind_for_conflict
- end
-
- # @return [Object] the current possibility that the resolution is trying
- # to activate
- def possibility
- possibilities.last
- end
-
- # @return [RequirementState] the current state the resolution is
- # operating upon
- def state
- states.last
- end
-
- # Creates and pushes the initial state for the resolution, based upon the
- # {#requested} dependencies
- # @return [void]
- def push_initial_state
- graph = DependencyGraph.new.tap do |dg|
- original_requested.each do |requested|
- vertex = dg.add_vertex(name_for(requested), nil, true)
- vertex.explicit_requirements << requested
- end
- dg.tag(:initial_state)
- end
-
- push_state_for_requirements(original_requested, true, graph)
- end
-
- # Unwinds the states stack because a conflict has been encountered
- # @return [void]
- def unwind_for_conflict
- details_for_unwind = build_details_for_unwind
- unwind_options = unused_unwind_options
- debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
- conflicts.tap do |c|
- sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
- raise_error_unless_state(c)
- activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
- state.conflicts = c
- state.unused_unwind_options = unwind_options
- filter_possibilities_after_unwind(details_for_unwind)
- index = states.size - 1
- @parents_of.each { |_, a| a.reject! { |i| i >= index } }
- state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
- end
- end
-
- # Raises a VersionConflict error, or any underlying error, if there is no
- # current state
- # @return [void]
- def raise_error_unless_state(conflicts)
- return if state
-
- error = conflicts.values.map(&:underlying_error).compact.first
- raise error || VersionConflict.new(conflicts, specification_provider)
- end
-
- # @return [UnwindDetails] Details of the nearest index to which we could unwind
- def build_details_for_unwind
- # Get the possible unwinds for the current conflict
- current_conflict = conflicts[name]
- binding_requirements = binding_requirements_for_conflict(current_conflict)
- unwind_details = unwind_options_for_requirements(binding_requirements)
-
- last_detail_for_current_unwind = unwind_details.sort.last
- current_detail = last_detail_for_current_unwind
-
- # Look for past conflicts that could be unwound to affect the
- # requirement tree for the current conflict
- all_reqs = last_detail_for_current_unwind.all_requirements
- all_reqs_size = all_reqs.size
- relevant_unused_unwinds = unused_unwind_options.select do |alternative|
- diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
- next if diff_reqs.size == all_reqs_size
- # Find the highest index unwind whilst looping through
- current_detail = alternative if alternative > current_detail
- alternative
- end
-
- # Add the current unwind options to the `unused_unwind_options` array.
- # The "used" option will be filtered out during `unwind_for_conflict`.
- state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
-
- # Update the requirements_unwound_to_instead on any relevant unused unwinds
- relevant_unused_unwinds.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
- unwind_details.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
-
- current_detail
- end
-
- # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
- # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
- # of resolving the passed requirements
- def unwind_options_for_requirements(binding_requirements)
- unwind_details = []
-
- trees = []
- binding_requirements.reverse_each do |r|
- partial_tree = [r]
- trees << partial_tree
- unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
-
- # If this requirement has alternative possibilities, check if any would
- # satisfy the other requirements that created this conflict
- requirement_state = find_state_for(r)
- if conflict_fixing_possibilities?(requirement_state, binding_requirements)
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Next, look at the parent of this requirement, and check if the requirement
- # could have been avoided if an alternative PossibilitySet had been chosen
- parent_r = parent_of(r)
- next if parent_r.nil?
- partial_tree.unshift(parent_r)
- requirement_state = find_state_for(parent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- parent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Finally, look at the grandparent and up of this requirement, looking
- # for any possibilities that wouldn't create their parent requirement
- grandparent_r = parent_of(parent_r)
- until grandparent_r.nil?
- partial_tree.unshift(grandparent_r)
- requirement_state = find_state_for(grandparent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- grandparent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
- parent_r = grandparent_r
- grandparent_r = parent_of(parent_r)
- end
- end
-
- unwind_details
- end
-
- # @param [DependencyState] state
- # @param [Array] binding_requirements array of requirements
- # @return [Boolean] whether or not the given state has any possibilities
- # that could satisfy the given requirements
- def conflict_fixing_possibilities?(state, binding_requirements)
- return false unless state
-
- state.possibilities.any? do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, binding_requirements)
- end
- end
- end
-
- # Filter's a state's possibilities to remove any that would not fix the
- # conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just
- # unwound from
- # @return [void]
- def filter_possibilities_after_unwind(unwind_details)
- return unless state && !state.possibilities.empty?
-
- if unwind_details.unwinding_to_primary_requirement?
- filter_possibilities_for_primary_unwind(unwind_details)
- else
- filter_possibilities_for_parent_unwind(unwind_details)
- end
- end
-
- # Filter's a state's possibilities to remove any that would not satisfy
- # the requirements in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_primary_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
- unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
-
- state.possibilities.reject! do |possibility_set|
- possibility_set.possibilities.none? do |poss|
- unwind_requirement_sets.any? do |requirements|
- possibility_satisfies_requirements?(poss, requirements)
- end
- end
- end
- end
-
- # @param [Object] possibility a single possibility
- # @param [Array] requirements an array of requirements
- # @return [Boolean] whether the possibility satisfies all of the
- # given requirements
- def possibility_satisfies_requirements?(possibility, requirements)
- name = name_for(possibility)
-
- activated.tag(:swap)
- activated.set_payload(name, possibility) if activated.vertex_named(name)
- satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
- activated.rewind_to(:swap)
-
- satisfied
- end
-
- # Filter's a state's possibilities to remove any that would (eventually)
- # create a requirement in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_parent_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
-
- primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
- parent_unwinds = unwinds_to_state.uniq - primary_unwinds
-
- allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
- states[unwind.state_index].possibilities.select do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
- end
- end
- end
-
- requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
-
- state.possibilities.reject! do |possibility_set|
- !allowed_possibility_sets.include?(possibility_set) &&
- (requirements_to_avoid - possibility_set.dependencies).empty?
- end
- end
-
- # @param [Conflict] conflict
- # @return [Array] minimal array of requirements that would cause the passed
- # conflict to occur.
- def binding_requirements_for_conflict(conflict)
- return [conflict.requirement] if conflict.possibility.nil?
-
- possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
-
- # When there's a `CircularDependency` error the conflicting requirement
- # (the one causing the circular) won't be `conflict.requirement`
- # (which won't be for the right state, because we won't have created it,
- # because it's circular).
- # We need to make sure we have that requirement in the conflict's list,
- # otherwise we won't be able to unwind properly, so we just return all
- # the requirements for the conflict.
- return possible_binding_requirements if conflict.underlying_error
-
- possibilities = search_for(conflict.requirement)
-
- # If all the requirements together don't filter out all possibilities,
- # then the only two requirements we need to consider are the initial one
- # (where the dependency's version was first chosen) and the last
- if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
- return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
- end
-
- # Loop through the possible binding requirements, removing each one
- # that doesn't bind. Use a `reverse_each` as we want the earliest set of
- # binding requirements, and don't use `reject!` as we wish to refine the
- # array *on each iteration*.
- binding_requirements = possible_binding_requirements.dup
- possible_binding_requirements.reverse_each do |req|
- next if req == conflict.requirement
- unless binding_requirement_in_set?(req, binding_requirements, possibilities)
- binding_requirements -= [req]
- end
- end
-
- binding_requirements
- end
-
- # @param [Object] requirement we wish to check
- # @param [Array] possible_binding_requirements array of requirements
- # @param [Array] possibilities array of possibilities the requirements will be used to filter
- # @return [Boolean] whether or not the given requirement is required to filter
- # out all elements of the array of possibilities.
- def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
- possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
- end
- end
-
- # @param [Object] requirement
- # @return [Object] the requirement that led to `requirement` being added
- # to the list of requirements.
- def parent_of(requirement)
- return unless requirement
- return unless index = @parents_of[requirement].last
- return unless parent_state = @states[index]
- parent_state.requirement
- end
-
- # @param [String] name
- # @return [Object] the requirement that led to a version of a possibility
- # with the given name being activated.
- def requirement_for_existing_name(name)
- return nil unless vertex = activated.vertex_named(name)
- return nil unless vertex.payload
- states.find { |s| s.name == name }.requirement
- end
-
- # @param [Object] requirement
- # @return [ResolutionState] the state whose `requirement` is the given
- # `requirement`.
- def find_state_for(requirement)
- return nil unless requirement
- states.find { |i| requirement == i.requirement }
- end
-
- # @param [Object] underlying_error
- # @return [Conflict] a {Conflict} that reflects the failure to activate
- # the {#possibility} in conjunction with the current {#state}
- def create_conflict(underlying_error = nil)
- vertex = activated.vertex_named(name)
- locked_requirement = locked_requirement_named(name)
-
- requirements = {}
- unless vertex.explicit_requirements.empty?
- requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
- end
- requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
- vertex.incoming_edges.each do |edge|
- (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
- end
-
- activated_by_name = {}
- activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
- conflicts[name] = Conflict.new(
- requirement,
- requirements,
- vertex.payload && vertex.payload.latest_version,
- possibility,
- locked_requirement,
- requirement_trees,
- activated_by_name,
- underlying_error
- )
- end
-
- # @return [Array<Array<Object>>] The different requirement
- # trees that led to every requirement for the current spec.
- def requirement_trees
- vertex = activated.vertex_named(name)
- vertex.requirements.map { |r| requirement_tree_for(r) }
- end
-
- # @param [Object] requirement
- # @return [Array<Object>] the list of requirements that led to
- # `requirement` being required.
- def requirement_tree_for(requirement)
- tree = []
- while requirement
- tree.unshift(requirement)
- requirement = parent_of(requirement)
- end
- tree
- end
-
- # Indicates progress roughly once every second
- # @return [void]
- def indicate_progress
- @iteration_counter += 1
- @progress_rate ||= resolver_ui.progress_rate
- if iteration_rate.nil?
- if Time.now - started_at >= @progress_rate
- self.iteration_rate = @iteration_counter
- end
- end
-
- if iteration_rate && (@iteration_counter % iteration_rate) == 0
- resolver_ui.indicate_progress
- end
- end
-
- # Calls the {#resolver_ui}'s {UI#debug} method
- # @param [Integer] depth the depth of the {#states} stack
- # @param [Proc] block a block that yields a {#to_s}
- # @return [void]
- def debug(depth = 0, &block)
- resolver_ui.debug(depth, &block)
- end
-
- # Attempts to activate the current {#possibility}
- # @return [void]
- def attempt_to_activate
- debug(depth) { 'Attempting to activate ' + possibility.to_s }
- existing_vertex = activated.vertex_named(name)
- if existing_vertex.payload
- debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
- attempt_to_filter_existing_spec(existing_vertex)
- else
- latest = possibility.latest_version
- possibility.possibilities.select! do |possibility|
- requirement_satisfied_by?(requirement, activated, possibility)
- end
- if possibility.latest_version.nil?
- # ensure there's a possibility for better error messages
- possibility.possibilities << latest if latest
- create_conflict
- unwind_for_conflict
- else
- activate_new_spec
- end
- end
- end
-
- # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
- # @return [void]
- def attempt_to_filter_existing_spec(vertex)
- filtered_set = filtered_possibility_set(vertex)
- if !filtered_set.possibilities.empty?
- activated.set_payload(name, filtered_set)
- new_requirements = requirements.dup
- push_state_for_requirements(new_requirements, false)
- else
- create_conflict
- debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
- unwind_for_conflict
- end
- end
-
- # Generates a filtered version of the existing vertex's `PossibilitySet` using the
- # current state's `requirement`
- # @param [Object] vertex existing vertex
- # @return [PossibilitySet] filtered possibility set
- def filtered_possibility_set(vertex)
- PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
- end
-
- # @param [String] requirement_name the spec name to search for
- # @return [Object] the locked spec named `requirement_name`, if one
- # is found on {#base}
- def locked_requirement_named(requirement_name)
- vertex = base.vertex_named(requirement_name)
- vertex && vertex.payload
- end
-
- # Add the current {#possibility} to the dependency graph of the current
- # {#state}
- # @return [void]
- def activate_new_spec
- conflicts.delete(name)
- debug(depth) { "Activated #{name} at #{possibility}" }
- activated.set_payload(name, possibility)
- require_nested_dependencies_for(possibility)
- end
-
- # Requires the dependencies that the recently activated spec has
- # @param [Object] possibility_set the PossibilitySet that has just been
- # activated
- # @return [void]
- def require_nested_dependencies_for(possibility_set)
- nested_dependencies = dependencies_for(possibility_set.latest_version)
- debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
- nested_dependencies.each do |d|
- activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
- parent_index = states.size - 1
- parents = @parents_of[d]
- parents << parent_index if parents.empty?
- end
-
- push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
- end
-
- # Pushes a new {DependencyState} that encapsulates both existing and new
- # requirements
- # @param [Array] new_requirements
- # @param [Boolean] requires_sort
- # @param [Object] new_activated
- # @return [void]
- def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
- new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
- new_requirement = nil
- loop do
- new_requirement = new_requirements.shift
- break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
- end
- new_name = new_requirement ? name_for(new_requirement) : ''.freeze
- possibilities = possibilities_for_requirement(new_requirement)
- handle_missing_or_push_dependency_state DependencyState.new(
- new_name, new_requirements, new_activated,
- new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
- )
- end
-
- # Checks a proposed requirement with any existing locked requirement
- # before generating an array of possibilities for it.
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibilities
- def possibilities_for_requirement(requirement, activated = self.activated)
- return [] unless requirement
- if locked_requirement_named(name_for(requirement))
- return locked_requirement_possibility_set(requirement, activated)
- end
-
- group_possibilities(search_for(requirement))
- end
-
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibility set containing only the locked requirement, if any
- def locked_requirement_possibility_set(requirement, activated = self.activated)
- all_possibilities = search_for(requirement)
- locked_requirement = locked_requirement_named(name_for(requirement))
-
- # Longwinded way to build a possibilities array with either the locked
- # requirement or nothing in it. Required, since the API for
- # locked_requirement isn't guaranteed.
- locked_possibilities = all_possibilities.select do |possibility|
- requirement_satisfied_by?(locked_requirement, activated, possibility)
- end
-
- group_possibilities(locked_possibilities)
- end
-
- # Build an array of PossibilitySets, with each element representing a group of
- # dependency versions that all have the same sub-dependency version constraints
- # and are contiguous.
- # @param [Array] possibilities an array of possibilities
- # @return [Array<PossibilitySet>] an array of possibility sets
- def group_possibilities(possibilities)
- possibility_sets = []
- current_possibility_set = nil
-
- possibilities.reverse_each do |possibility|
- dependencies = dependencies_for(possibility)
- if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
- current_possibility_set.possibilities.unshift(possibility)
- else
- possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
- current_possibility_set = possibility_sets.first
- end
- end
-
- possibility_sets
- end
-
- # Pushes a new {DependencyState}.
- # If the {#specification_provider} says to
- # {SpecificationProvider#allow_missing?} that particular requirement, and
- # there are no possibilities for that requirement, then `state` is not
- # pushed, and the vertex in {#activated} is removed, and we continue
- # resolving the remaining requirements.
- # @param [DependencyState] state
- # @return [void]
- def handle_missing_or_push_dependency_state(state)
- if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
- state.activated.detach_vertex_named(state.name)
- push_state_for_requirements(state.requirements.dup, false, state.activated)
- else
- states.push(state).tap { activated.tag(state) }
- end
- end
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb
deleted file mode 100644
index 95eaab5991..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'dependency_graph'
-
-module Bundler::Molinillo
- # This class encapsulates a dependency resolver.
- # The resolver is responsible for determining which set of dependencies to
- # activate, with feedback from the {#specification_provider}
- #
- #
- class Resolver
- require_relative 'resolution'
-
- # @return [SpecificationProvider] the specification provider used
- # in the resolution process
- attr_reader :specification_provider
-
- # @return [UI] the UI module used to communicate back to the user
- # during the resolution process
- attr_reader :resolver_ui
-
- # Initializes a new resolver.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui
- # see {#resolver_ui}
- def initialize(specification_provider, resolver_ui)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- end
-
- # Resolves the requested dependencies into a {DependencyGraph},
- # locking to the base dependency graph (if specified)
- # @param [Array] requested an array of 'requested' dependencies that the
- # {#specification_provider} can understand
- # @param [DependencyGraph,nil] base the base dependency graph to which
- # dependencies should be 'locked'
- def resolve(requested, base = DependencyGraph.new)
- Resolution.new(specification_provider,
- resolver_ui,
- requested,
- base).
- resolve
- end
- end
-end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb
deleted file mode 100644
index 68fa1f54e3..0000000000
--- a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler::Molinillo
- # A state that a {Resolution} can be in
- # @attr [String] name the name of the current requirement
- # @attr [Array<Object>] requirements currently unsatisfied requirements
- # @attr [DependencyGraph] activated the graph of activated dependencies
- # @attr [Object] requirement the current requirement
- # @attr [Object] possibilities the possibilities to satisfy the current requirement
- # @attr [Integer] depth the depth of the resolution
- # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
- # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored
- ResolutionState = Struct.new(
- :name,
- :requirements,
- :activated,
- :requirement,
- :possibilities,
- :depth,
- :conflicts,
- :unused_unwind_options
- )
-
- class ResolutionState
- # Returns an empty resolution state
- # @return [ResolutionState] an empty state
- def self.empty
- new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
- end
- end
-
- # A state that encapsulates a set of {#requirements} with an {Array} of
- # possibilities
- class DependencyState < ResolutionState
- # Removes a possibility from `self`
- # @return [PossibilityState] a state with a single possibility,
- # the possibility that was removed from `self`
- def pop_possibility_state
- PossibilityState.new(
- name,
- requirements.dup,
- activated,
- requirement,
- [possibilities.pop],
- depth + 1,
- conflicts.dup,
- unused_unwind_options.dup
- ).tap do |state|
- state.activated.tag(state)
- end
- end
- end
-
- # A state that encapsulates a single possibility to fulfill the given
- # {#requirement}
- class PossibilityState < ResolutionState
- end
-end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub.rb
new file mode 100644
index 0000000000..eaaba3fc98
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub.rb
@@ -0,0 +1,31 @@
+require_relative "pub_grub/package"
+require_relative "pub_grub/static_package_source"
+require_relative "pub_grub/term"
+require_relative "pub_grub/version_range"
+require_relative "pub_grub/version_constraint"
+require_relative "pub_grub/version_union"
+require_relative "pub_grub/version_solver"
+require_relative "pub_grub/incompatibility"
+require_relative 'pub_grub/solve_failure'
+require_relative 'pub_grub/failure_writer'
+require_relative 'pub_grub/version'
+
+module Bundler::PubGrub
+ class << self
+ attr_writer :logger
+
+ def logger
+ @logger || default_logger
+ end
+
+ private
+
+ def default_logger
+ require "logger"
+
+ logger = ::Logger.new(STDERR)
+ logger.level = $DEBUG ? ::Logger::DEBUG : ::Logger::WARN
+ @logger = logger
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb
new file mode 100644
index 0000000000..2236a97b5b
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb
@@ -0,0 +1,20 @@
+module Bundler::PubGrub
+ class Assignment
+ attr_reader :term, :cause, :decision_level, :index
+ def initialize(term, cause, decision_level, index)
+ @term = term
+ @cause = cause
+ @decision_level = decision_level
+ @index = index
+ end
+
+ def self.decision(package, version, decision_level, index)
+ term = Term.new(VersionConstraint.exact(package, version), true)
+ new(term, :decision, decision_level, index)
+ end
+
+ def decision?
+ cause == :decision
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb
new file mode 100644
index 0000000000..dce20d37ad
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb
@@ -0,0 +1,189 @@
+require_relative 'version_constraint'
+require_relative 'incompatibility'
+
+module Bundler::PubGrub
+ # Types:
+ #
+ # Where possible, Bundler::PubGrub will accept user-defined types, so long as they quack.
+ #
+ # ## "Package":
+ #
+ # This class will be used to represent the various packages being solved for.
+ # .to_s will be called when displaying errors and debugging info, it should
+ # probably return the package's name.
+ # It must also have a reasonable definition of #== and #hash
+ #
+ # Example classes: String ("rails")
+ #
+ #
+ # ## "Version":
+ #
+ # This class will be used to represent a single version number.
+ #
+ # Versions don't need to store their associated package, however they will
+ # only be compared against other versions of the same package.
+ #
+ # It must be Comparible (and implement <=> reasonably)
+ #
+ # Example classes: Gem::Version, Integer
+ #
+ #
+ # ## "Dependency"
+ #
+ # This class represents the requirement one package has on another. It is
+ # returned by dependencies_for(package, version) and will be passed to
+ # parse_dependency to convert it to a format Bundler::PubGrub understands.
+ #
+ # It must also have a reasonable definition of #==
+ #
+ # Example classes: String ("~> 1.0"), Gem::Requirement
+ #
+ class BasicPackageSource
+ # Override me!
+ #
+ # This is called per package to find all possible versions of a package.
+ #
+ # It is called at most once per-package
+ #
+ # Returns: Array of versions for a package, in preferred order of selection
+ def all_versions_for(package)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # Returns: Hash in the form of { package => requirement, ... }
+ def dependencies_for(package, version)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # Convert a (user-defined) dependency into a format Bundler::PubGrub understands.
+ #
+ # Package is passed to this method but for many implementations is not
+ # needed.
+ #
+ # Returns: either a Bundler::PubGrub::VersionRange, Bundler::PubGrub::VersionUnion, or a
+ # Bundler::PubGrub::VersionConstraint
+ def parse_dependency(package, dependency)
+ raise NotImplementedError
+ end
+
+ # Override me!
+ #
+ # If not overridden, this will call dependencies_for with the root package.
+ #
+ # Returns: Hash in the form of { package => requirement, ... } (see dependencies_for)
+ def root_dependencies
+ dependencies_for(@root_package, @root_version)
+ end
+
+ # Override me (maybe)
+ #
+ # If not overridden, the order returned by all_versions_for will be used
+ #
+ # Returns: Array of versions in preferred order
+ def sort_versions_by_preferred(package, sorted_versions)
+ indexes = @version_indexes[package]
+ sorted_versions.sort_by { |version| indexes[version] }
+ end
+
+ def initialize
+ @root_package = Package.root
+ @root_version = Package.root_version
+
+ @cached_versions = Hash.new do |h,k|
+ if k == @root_package
+ h[k] = [@root_version]
+ else
+ h[k] = all_versions_for(k)
+ end
+ end
+ @sorted_versions = Hash.new { |h,k| h[k] = @cached_versions[k].sort }
+ @version_indexes = Hash.new { |h,k| h[k] = @cached_versions[k].each.with_index.to_h }
+
+ @cached_dependencies = Hash.new do |packages, package|
+ if package == @root_package
+ packages[package] = {
+ @root_version => root_dependencies
+ }
+ else
+ packages[package] = Hash.new do |versions, version|
+ versions[version] = dependencies_for(package, version)
+ end
+ end
+ end
+ end
+
+ def versions_for(package, range=VersionRange.any)
+ versions = range.select_versions(@sorted_versions[package])
+
+ # Conditional avoids (among other things) calling
+ # sort_versions_by_preferred with the root package
+ if versions.size > 1
+ sort_versions_by_preferred(package, versions)
+ else
+ versions
+ end
+ end
+
+ def no_versions_incompatibility_for(_package, unsatisfied_term)
+ cause = Incompatibility::NoVersions.new(unsatisfied_term)
+
+ Incompatibility.new([unsatisfied_term], cause: cause)
+ end
+
+ def incompatibilities_for(package, version)
+ package_deps = @cached_dependencies[package]
+ sorted_versions = @sorted_versions[package]
+ package_deps[version].map do |dep_package, dep_constraint_name|
+ low = high = sorted_versions.index(version)
+
+ # find version low such that all >= low share the same dep
+ while low > 0 &&
+ package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint_name
+ low -= 1
+ end
+ low =
+ if low == 0
+ nil
+ else
+ sorted_versions[low]
+ end
+
+ # find version high such that all < high share the same dep
+ while high < sorted_versions.length &&
+ package_deps[sorted_versions[high]][dep_package] == dep_constraint_name
+ high += 1
+ end
+ high =
+ if high == sorted_versions.length
+ nil
+ else
+ sorted_versions[high]
+ end
+
+ range = VersionRange.new(min: low, max: high, include_min: true)
+
+ self_constraint = VersionConstraint.new(package, range: range)
+
+ if !@packages.include?(dep_package)
+ # no such package -> this version is invalid
+ end
+
+ dep_constraint = parse_dependency(dep_package, dep_constraint_name)
+ if !dep_constraint
+ # falsey indicates this dependency was invalid
+ cause = Bundler::PubGrub::Incompatibility::InvalidDependency.new(dep_package, dep_constraint_name)
+ return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
+ elsif !dep_constraint.is_a?(VersionConstraint)
+ # Upgrade range/union to VersionConstraint
+ dep_constraint = VersionConstraint.new(dep_package, range: dep_constraint)
+ end
+
+ Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb
new file mode 100644
index 0000000000..ee099b23f4
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb
@@ -0,0 +1,182 @@
+module Bundler::PubGrub
+ class FailureWriter
+ def initialize(root)
+ @root = root
+
+ # { Incompatibility => Integer }
+ @derivations = {}
+
+ # [ [ String, Integer or nil ] ]
+ @lines = []
+
+ # { Incompatibility => Integer }
+ @line_numbers = {}
+
+ count_derivations(root)
+ end
+
+ def write
+ return @root.to_s unless @root.conflict?
+
+ visit(@root)
+
+ padding = @line_numbers.empty? ? 0 : "(#{@line_numbers.values.last}) ".length
+
+ @lines.map do |message, number|
+ next "" if message.empty?
+
+ lead = number ? "(#{number}) " : ""
+ lead = lead.ljust(padding)
+ message = message.gsub("\n", "\n" + " " * (padding + 2))
+ "#{lead}#{message}"
+ end.join("\n")
+ end
+
+ private
+
+ def write_line(incompatibility, message, numbered:)
+ if numbered
+ number = @line_numbers.length + 1
+ @line_numbers[incompatibility] = number
+ end
+
+ @lines << [message, number]
+ end
+
+ def visit(incompatibility, conclusion: false)
+ raise unless incompatibility.conflict?
+
+ numbered = conclusion || @derivations[incompatibility] > 1;
+ conjunction = conclusion || incompatibility == @root ? "So," : "And"
+
+ cause = incompatibility.cause
+
+ if cause.conflict.conflict? && cause.other.conflict?
+ conflict_line = @line_numbers[cause.conflict]
+ other_line = @line_numbers[cause.other]
+
+ if conflict_line && other_line
+ write_line(
+ incompatibility,
+ "Because #{cause.conflict} (#{conflict_line})\nand #{cause.other} (#{other_line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ elsif conflict_line || other_line
+ with_line = conflict_line ? cause.conflict : cause.other
+ without_line = conflict_line ? cause.other : cause.conflict
+ line = @line_numbers[with_line]
+
+ visit(without_line);
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{with_line} (#{line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ single_line_conflict = single_line?(cause.conflict.cause)
+ single_line_other = single_line?(cause.other.cause)
+
+ if single_line_conflict || single_line_other
+ first = single_line_other ? cause.conflict : cause.other
+ second = single_line_other ? cause.other : cause.conflict
+ visit(first)
+ visit(second)
+ write_line(
+ incompatibility,
+ "Thus, #{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ visit(cause.conflict, conclusion: true)
+ @lines << ["", nil]
+ visit(cause.other)
+
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{cause.conflict} (#{@line_numbers[cause.conflict]}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ end
+ elsif cause.conflict.conflict? || cause.other.conflict?
+ derived = cause.conflict.conflict? ? cause.conflict : cause.other
+ ext = cause.conflict.conflict? ? cause.other : cause.conflict
+
+ derived_line = @line_numbers[derived]
+ if derived_line
+ write_line(
+ incompatibility,
+ "Because #{ext}\nand #{derived} (#{derived_line}),\n#{incompatibility}.",
+ numbered: numbered
+ )
+ elsif collapsible?(derived)
+ derived_cause = derived.cause
+ if derived_cause.conflict.conflict?
+ collapsed_derived = derived_cause.conflict
+ collapsed_ext = derived_cause.other
+ else
+ collapsed_derived = derived_cause.other
+ collapsed_ext = derived_cause.conflict
+ end
+
+ visit(collapsed_derived)
+
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{collapsed_ext}\nand #{ext},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ else
+ visit(derived)
+ write_line(
+ incompatibility,
+ "#{conjunction} because #{ext},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ else
+ write_line(
+ incompatibility,
+ "Because #{cause.conflict}\nand #{cause.other},\n#{incompatibility}.",
+ numbered: numbered
+ )
+ end
+ end
+
+ def single_line?(cause)
+ !cause.conflict.conflict? && !cause.other.conflict?
+ end
+
+ def collapsible?(incompatibility)
+ return false if @derivations[incompatibility] > 1
+
+ cause = incompatibility.cause
+ # If incompatibility is derived from two derived incompatibilities,
+ # there are too many transitive causes to display concisely.
+ return false if cause.conflict.conflict? && cause.other.conflict?
+
+ # If incompatibility is derived from two external incompatibilities, it
+ # tends to be confusing to collapse it.
+ return false unless cause.conflict.conflict? || cause.other.conflict?
+
+ # If incompatibility's internal cause is numbered, collapsing it would
+ # get too noisy.
+ complex = cause.conflict.conflict? ? cause.conflict : cause.other
+
+ !@line_numbers.has_key?(complex)
+ end
+
+ def count_derivations(incompatibility)
+ if @derivations.has_key?(incompatibility)
+ @derivations[incompatibility] += 1
+ else
+ @derivations[incompatibility] = 1
+ if incompatibility.conflict?
+ cause = incompatibility.cause
+ count_derivations(cause.conflict)
+ count_derivations(cause.other)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb
new file mode 100644
index 0000000000..239eaf3401
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb
@@ -0,0 +1,150 @@
+module Bundler::PubGrub
+ class Incompatibility
+ ConflictCause = Struct.new(:incompatibility, :satisfier) do
+ alias_method :conflict, :incompatibility
+ alias_method :other, :satisfier
+ end
+
+ InvalidDependency = Struct.new(:package, :constraint) do
+ end
+
+ NoVersions = Struct.new(:constraint) do
+ end
+
+ attr_reader :terms, :cause
+
+ def initialize(terms, cause:, custom_explanation: nil)
+ @cause = cause
+ @terms = cleanup_terms(terms)
+ @custom_explanation = custom_explanation
+
+ if cause == :dependency && @terms.length != 2
+ raise ArgumentError, "a dependency Incompatibility must have exactly two terms. Got #{@terms.inspect}"
+ end
+ end
+
+ def hash
+ cause.hash ^ terms.hash
+ end
+
+ def eql?(other)
+ cause.eql?(other.cause) &&
+ terms.eql?(other.terms)
+ end
+
+ def failure?
+ terms.empty? || (terms.length == 1 && Package.root?(terms[0].package) && terms[0].positive?)
+ end
+
+ def conflict?
+ ConflictCause === cause
+ end
+
+ # Returns all external incompatibilities in this incompatibility's
+ # derivation graph
+ def external_incompatibilities
+ if conflict?
+ [
+ cause.conflict,
+ cause.other
+ ].flat_map(&:external_incompatibilities)
+ else
+ [this]
+ end
+ end
+
+ def to_s
+ return @custom_explanation if @custom_explanation
+
+ case cause
+ when :root
+ "(root dependency)"
+ when :dependency
+ "#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
+ when Bundler::PubGrub::Incompatibility::InvalidDependency
+ "#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
+ when Bundler::PubGrub::Incompatibility::NoVersions
+ "no versions satisfy #{cause.constraint}"
+ when Bundler::PubGrub::Incompatibility::ConflictCause
+ if failure?
+ "version solving has failed"
+ elsif terms.length == 1
+ term = terms[0]
+ if term.positive?
+ if term.constraint.any?
+ "#{term.package} cannot be used"
+ else
+ "#{term.to_s(allow_every: true)} cannot be used"
+ end
+ else
+ "#{term.invert} is required"
+ end
+ else
+ if terms.all?(&:positive?)
+ if terms.length == 2
+ "#{terms[0].to_s(allow_every: true)} is incompatible with #{terms[1]}"
+ else
+ "one of #{terms.map(&:to_s).join(" or ")} must be false"
+ end
+ elsif terms.all?(&:negative?)
+ if terms.length == 2
+ "either #{terms[0].invert} or #{terms[1].invert}"
+ else
+ "one of #{terms.map(&:invert).join(" or ")} must be true";
+ end
+ else
+ positive = terms.select(&:positive?)
+ negative = terms.select(&:negative?).map(&:invert)
+
+ if positive.length == 1
+ "#{positive[0].to_s(allow_every: true)} requires #{negative.join(" or ")}"
+ else
+ "if #{positive.join(" and ")} then #{negative.join(" or ")}"
+ end
+ end
+ end
+ else
+ raise "unhandled cause: #{cause.inspect}"
+ end
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def pretty_print(q)
+ q.group 2, "#<#{self.class}", ">" do
+ q.breakable
+ q.text to_s
+
+ q.breakable
+ q.text " caused by "
+ q.pp @cause
+ end
+ end
+
+ private
+
+ def cleanup_terms(terms)
+ terms.each do |term|
+ raise "#{term.inspect} must be a term" unless term.is_a?(Term)
+ end
+
+ if terms.length != 1 && ConflictCause === cause
+ terms = terms.reject do |term|
+ term.positive? && Package.root?(term.package)
+ end
+ end
+
+ # Optimized simple cases
+ return terms if terms.length <= 1
+ return terms if terms.length == 2 && terms[0].package != terms[1].package
+
+ terms.group_by(&:package).map do |package, common_terms|
+ common_terms.inject do |acc, term|
+ acc.intersect(term)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb
new file mode 100644
index 0000000000..efb9d3da16
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class Package
+
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def inspect
+ "#<#{self.class} #{name.inspect}>"
+ end
+
+ def <=>(other)
+ name <=> other.name
+ end
+
+ ROOT = Package.new(:root)
+ ROOT_VERSION = 0
+
+ def self.root
+ ROOT
+ end
+
+ def self.root_version
+ ROOT_VERSION
+ end
+
+ def self.root?(package)
+ if package.respond_to?(:root?)
+ package.root?
+ else
+ package == root
+ end
+ end
+
+ def to_s
+ name.to_s
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb
new file mode 100644
index 0000000000..4c4b8ca844
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb
@@ -0,0 +1,121 @@
+require_relative 'assignment'
+
+module Bundler::PubGrub
+ class PartialSolution
+ attr_reader :assignments, :decisions
+ attr_reader :attempted_solutions
+
+ def initialize
+ reset!
+
+ @attempted_solutions = 1
+ @backtracking = false
+ end
+
+ def decision_level
+ @decisions.length
+ end
+
+ def relation(term)
+ package = term.package
+ return :overlap if !@terms.key?(package)
+
+ @relation_cache[package][term] ||=
+ @terms[package].relation(term)
+ end
+
+ def satisfies?(term)
+ relation(term) == :subset
+ end
+
+ def derive(term, cause)
+ add_assignment(Assignment.new(term, cause, decision_level, assignments.length))
+ end
+
+ def satisfier(term)
+ assignment =
+ @assignments_by[term.package].bsearch do |assignment_by|
+ @cumulative_assignments[assignment_by].satisfies?(term)
+ end
+
+ assignment || raise("#{term} unsatisfied")
+ end
+
+ # A list of unsatisfied terms
+ def unsatisfied
+ @required.keys.reject do |package|
+ @decisions.key?(package)
+ end.map do |package|
+ @terms[package]
+ end
+ end
+
+ def decide(package, version)
+ @attempted_solutions += 1 if @backtracking
+ @backtracking = false;
+
+ decisions[package] = version
+ assignment = Assignment.decision(package, version, decision_level, assignments.length)
+ add_assignment(assignment)
+ end
+
+ def backtrack(previous_level)
+ @backtracking = true
+
+ new_assignments = assignments.select do |assignment|
+ assignment.decision_level <= previous_level
+ end
+
+ new_decisions = Hash[decisions.first(previous_level)]
+
+ reset!
+
+ @decisions = new_decisions
+
+ new_assignments.each do |assignment|
+ add_assignment(assignment)
+ end
+ end
+
+ private
+
+ def reset!
+ # { Array<Assignment> }
+ @assignments = []
+
+ # { Package => Array<Assignment> }
+ @assignments_by = Hash.new { |h,k| h[k] = [] }
+ @cumulative_assignments = {}.compare_by_identity
+
+ # { Package => Package::Version }
+ @decisions = {}
+
+ # { Package => Term }
+ @terms = {}
+ @relation_cache = Hash.new { |h,k| h[k] = {} }
+
+ # { Package => Boolean }
+ @required = {}
+ end
+
+ def add_assignment(assignment)
+ term = assignment.term
+ package = term.package
+
+ @assignments << assignment
+ @assignments_by[package] << assignment
+
+ @required[package] = true if term.positive?
+
+ if @terms.key?(package)
+ old_term = @terms[package]
+ @terms[package] = old_term.intersect(term)
+ else
+ @terms[package] = term
+ end
+ @relation_cache[package].clear
+
+ @cumulative_assignments[assignment] = @terms[package]
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb
new file mode 100644
index 0000000000..245c23be22
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb
@@ -0,0 +1,45 @@
+module Bundler::PubGrub
+ module RubyGems
+ extend self
+
+ def requirement_to_range(requirement)
+ ranges = requirement.requirements.map do |(op, ver)|
+ case op
+ when "~>"
+ name = "~> #{ver}"
+ bump = ver.class.new(ver.bump.to_s + ".A")
+ VersionRange.new(name: name, min: ver, max: bump, include_min: true)
+ when ">"
+ VersionRange.new(min: ver)
+ when ">="
+ VersionRange.new(min: ver, include_min: true)
+ when "<"
+ VersionRange.new(max: ver)
+ when "<="
+ VersionRange.new(max: ver, include_max: true)
+ when "="
+ VersionRange.new(min: ver, max: ver, include_min: true, include_max: true)
+ when "!="
+ VersionRange.new(min: ver, max: ver, include_min: true, include_max: true).invert
+ else
+ raise "bad version specifier: #{op}"
+ end
+ end
+
+ ranges.inject(&:intersect)
+ end
+
+ def requirement_to_constraint(package, requirement)
+ Bundler::PubGrub::VersionConstraint.new(package, range: requirement_to_range(requirement))
+ end
+
+ def parse_range(dep)
+ requirement_to_range(Gem::Requirement.new(dep))
+ end
+
+ def parse_constraint(package, dep)
+ range = parse_range(dep)
+ Bundler::PubGrub::VersionConstraint.new(package, range: range)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb
new file mode 100644
index 0000000000..961a7a7c0c
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb
@@ -0,0 +1,19 @@
+require_relative 'failure_writer'
+
+module Bundler::PubGrub
+ class SolveFailure < StandardError
+ attr_reader :incompatibility
+
+ def initialize(incompatibility)
+ @incompatibility = incompatibility
+ end
+
+ def to_s
+ "Could not find compatible versions\n\n#{explanation}"
+ end
+
+ def explanation
+ @explanation ||= FailureWriter.new(@incompatibility).write
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
new file mode 100644
index 0000000000..4bf61461b2
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
@@ -0,0 +1,60 @@
+require_relative 'package'
+require_relative 'version_constraint'
+require_relative 'incompatibility'
+require_relative 'basic_package_source'
+
+module Bundler::PubGrub
+ class StaticPackageSource < BasicPackageSource
+ class DSL
+ def initialize(packages, root_deps)
+ @packages = packages
+ @root_deps = root_deps
+ end
+
+ def root(deps:)
+ @root_deps.update(deps)
+ end
+
+ def add(name, version, deps: {})
+ version = Gem::Version.new(version)
+ @packages[name] ||= {}
+ raise ArgumentError, "#{name} #{version} declared twice" if @packages[name].key?(version)
+ @packages[name][version] = clean_deps(name, version, deps)
+ end
+
+ private
+
+ # Exclude redundant self-referencing dependencies
+ def clean_deps(name, version, deps)
+ deps.reject {|dep_name, req| name == dep_name && Bundler::PubGrub::RubyGems.parse_range(req).include?(version) }
+ end
+ end
+
+ def initialize
+ @root_deps = {}
+ @packages = {}
+
+ yield DSL.new(@packages, @root_deps)
+
+ super()
+ end
+
+ def all_versions_for(package)
+ @packages[package].keys
+ end
+
+ def root_dependencies
+ @root_deps
+ end
+
+ def dependencies_for(package, version)
+ @packages[package][version]
+ end
+
+ def parse_dependency(package, dependency)
+ return false unless @packages.key?(package)
+
+ Bundler::PubGrub::RubyGems.parse_constraint(package, dependency)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb
new file mode 100644
index 0000000000..1d0f763378
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb
@@ -0,0 +1,105 @@
+module Bundler::PubGrub
+ class Term
+ attr_reader :package, :constraint, :positive
+
+ def initialize(constraint, positive)
+ @constraint = constraint
+ @package = @constraint.package
+ @positive = positive
+ end
+
+ def to_s(allow_every: false)
+ if positive
+ @constraint.to_s(allow_every: allow_every)
+ else
+ "not #{@constraint}"
+ end
+ end
+
+ def hash
+ constraint.hash ^ positive.hash
+ end
+
+ def eql?(other)
+ positive == other.positive &&
+ constraint.eql?(other.constraint)
+ end
+
+ def invert
+ self.class.new(@constraint, !@positive)
+ end
+ alias_method :inverse, :invert
+
+ def intersect(other)
+ raise ArgumentError, "packages must match" if package != other.package
+
+ if positive? && other.positive?
+ self.class.new(constraint.intersect(other.constraint), true)
+ elsif negative? && other.negative?
+ self.class.new(constraint.union(other.constraint), false)
+ else
+ positive = positive? ? self : other
+ negative = negative? ? self : other
+ self.class.new(positive.constraint.intersect(negative.constraint.invert), true)
+ end
+ end
+
+ def difference(other)
+ intersect(other.invert)
+ end
+
+ def relation(other)
+ if positive? && other.positive?
+ constraint.relation(other.constraint)
+ elsif negative? && other.positive?
+ if constraint.allows_all?(other.constraint)
+ :disjoint
+ else
+ :overlap
+ end
+ elsif positive? && other.negative?
+ if !other.constraint.allows_any?(constraint)
+ :subset
+ elsif other.constraint.allows_all?(constraint)
+ :disjoint
+ else
+ :overlap
+ end
+ elsif negative? && other.negative?
+ if constraint.allows_all?(other.constraint)
+ :subset
+ else
+ :overlap
+ end
+ else
+ raise
+ end
+ end
+
+ def normalized_constraint
+ @normalized_constraint ||= positive ? constraint : constraint.invert
+ end
+
+ def satisfies?(other)
+ raise ArgumentError, "packages must match" unless package == other.package
+
+ relation(other) == :subset
+ end
+
+ def positive?
+ @positive
+ end
+
+ def negative?
+ !positive?
+ end
+
+ def empty?
+ @empty ||= normalized_constraint.empty?
+ end
+
+ def inspect
+ "#<#{self.class} #{self}>"
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb
new file mode 100644
index 0000000000..d7984b3863
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb
@@ -0,0 +1,3 @@
+module Bundler::PubGrub
+ VERSION = "0.5.0"
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb
new file mode 100644
index 0000000000..b71f3eaf53
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb
@@ -0,0 +1,129 @@
+require_relative 'version_range'
+
+module Bundler::PubGrub
+ class VersionConstraint
+ attr_reader :package, :range
+
+ # @param package [Bundler::PubGrub::Package]
+ # @param range [Bundler::PubGrub::VersionRange]
+ def initialize(package, range: nil)
+ @package = package
+ @range = range
+ end
+
+ def hash
+ package.hash ^ range.hash
+ end
+
+ def ==(other)
+ package == other.package &&
+ range == other.range
+ end
+
+ def eql?(other)
+ package.eql?(other.package) &&
+ range.eql?(other.range)
+ end
+
+ class << self
+ def exact(package, version)
+ range = VersionRange.new(min: version, max: version, include_min: true, include_max: true)
+ new(package, range: range)
+ end
+
+ def any(package)
+ new(package, range: VersionRange.any)
+ end
+
+ def empty(package)
+ new(package, range: VersionRange.empty)
+ end
+ end
+
+ def intersect(other)
+ unless package == other.package
+ raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
+ end
+
+ self.class.new(package, range: range.intersect(other.range))
+ end
+
+ def union(other)
+ unless package == other.package
+ raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
+ end
+
+ self.class.new(package, range: range.union(other.range))
+ end
+
+ def invert
+ new_range = range.invert
+ self.class.new(package, range: new_range)
+ end
+
+ def difference(other)
+ intersect(other.invert)
+ end
+
+ def allows_all?(other)
+ range.allows_all?(other.range)
+ end
+
+ def allows_any?(other)
+ range.intersects?(other.range)
+ end
+
+ def subset?(other)
+ other.allows_all?(self)
+ end
+
+ def overlap?(other)
+ other.allows_any?(self)
+ end
+
+ def disjoint?(other)
+ !overlap?(other)
+ end
+
+ def relation(other)
+ if subset?(other)
+ :subset
+ elsif overlap?(other)
+ :overlap
+ else
+ :disjoint
+ end
+ end
+
+ def to_s(allow_every: false)
+ if Package.root?(package)
+ package.to_s
+ elsif allow_every && any?
+ "every version of #{package}"
+ else
+ "#{package} #{constraint_string}"
+ end
+ end
+
+ def constraint_string
+ if any?
+ ">= 0"
+ else
+ range.to_s
+ end
+ end
+
+ def empty?
+ range.empty?
+ end
+
+ # Does this match every version of the package
+ def any?
+ range.any?
+ end
+
+ def inspect
+ "#<#{self.class} #{self}>"
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb
new file mode 100644
index 0000000000..8d73c3f7b5
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb
@@ -0,0 +1,411 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class VersionRange
+ attr_reader :min, :max, :include_min, :include_max
+
+ alias_method :include_min?, :include_min
+ alias_method :include_max?, :include_max
+
+ class Empty < VersionRange
+ undef_method :min, :max
+ undef_method :include_min, :include_min?
+ undef_method :include_max, :include_max?
+
+ def initialize
+ end
+
+ def empty?
+ true
+ end
+
+ def eql?(other)
+ other.empty?
+ end
+
+ def hash
+ [].hash
+ end
+
+ def intersects?(_)
+ false
+ end
+
+ def intersect(other)
+ self
+ end
+
+ def allows_all?(other)
+ other.empty?
+ end
+
+ def include?(_)
+ false
+ end
+
+ def any?
+ false
+ end
+
+ def to_s
+ "(no versions)"
+ end
+
+ def ==(other)
+ other.class == self.class
+ end
+
+ def invert
+ VersionRange.any
+ end
+
+ def select_versions(_)
+ []
+ end
+ end
+
+ EMPTY = Empty.new
+ Empty.singleton_class.undef_method(:new)
+
+ def self.empty
+ EMPTY
+ end
+
+ def self.any
+ new
+ end
+
+ def initialize(min: nil, max: nil, include_min: false, include_max: false, name: nil)
+ @min = min
+ @max = max
+ @include_min = include_min
+ @include_max = include_max
+ @name = name
+ end
+
+ def hash
+ @hash ||= min.hash ^ max.hash ^ include_min.hash ^ include_max.hash
+ end
+
+ def eql?(other)
+ if other.is_a?(VersionRange)
+ !other.empty? &&
+ min.eql?(other.min) &&
+ max.eql?(other.max) &&
+ include_min.eql?(other.include_min) &&
+ include_max.eql?(other.include_max)
+ else
+ ranges.eql?(other.ranges)
+ end
+ end
+
+ def ranges
+ [self]
+ end
+
+ def include?(version)
+ compare_version(version) == 0
+ end
+
+ # Partitions passed versions into [lower, within, higher]
+ #
+ # versions must be sorted
+ def partition_versions(versions)
+ min_index =
+ if !min || versions.empty?
+ 0
+ elsif include_min?
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= min }
+ else
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > min }
+ end
+
+ lower = versions.slice(0, min_index)
+ versions = versions.slice(min_index, versions.size)
+
+ max_index =
+ if !max || versions.empty?
+ versions.size
+ elsif include_max?
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > max }
+ else
+ (0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= max }
+ end
+
+ [
+ lower,
+ versions.slice(0, max_index),
+ versions.slice(max_index, versions.size)
+ ]
+ end
+
+ # Returns versions which are included by this range.
+ #
+ # versions must be sorted
+ def select_versions(versions)
+ return versions if any?
+
+ partition_versions(versions)[1]
+ end
+
+ def compare_version(version)
+ if min
+ case version <=> min
+ when -1
+ return -1
+ when 0
+ return -1 if !include_min
+ when 1
+ end
+ end
+
+ if max
+ case version <=> max
+ when -1
+ when 0
+ return 1 if !include_max
+ when 1
+ return 1
+ end
+ end
+
+ 0
+ end
+
+ def strictly_lower?(other)
+ return false if !max || !other.min
+
+ case max <=> other.min
+ when 0
+ !include_max || !other.include_min
+ when -1
+ true
+ when 1
+ false
+ end
+ end
+
+ def strictly_higher?(other)
+ other.strictly_lower?(self)
+ end
+
+ def intersects?(other)
+ return false if other.empty?
+ return other.intersects?(self) if other.is_a?(VersionUnion)
+ !strictly_lower?(other) && !strictly_higher?(other)
+ end
+ alias_method :allows_any?, :intersects?
+
+ def intersect(other)
+ return other if other.empty?
+ return other.intersect(self) if other.is_a?(VersionUnion)
+
+ min_range =
+ if !min
+ other
+ elsif !other.min
+ self
+ else
+ case min <=> other.min
+ when 0
+ include_min ? other : self
+ when -1
+ other
+ when 1
+ self
+ end
+ end
+
+ max_range =
+ if !max
+ other
+ elsif !other.max
+ self
+ else
+ case max <=> other.max
+ when 0
+ include_max ? other : self
+ when -1
+ self
+ when 1
+ other
+ end
+ end
+
+ if !min_range.equal?(max_range) && min_range.min && max_range.max
+ case min_range.min <=> max_range.max
+ when -1
+ when 0
+ if !min_range.include_min || !max_range.include_max
+ return EMPTY
+ end
+ when 1
+ return EMPTY
+ end
+ end
+
+ VersionRange.new(
+ min: min_range.min,
+ include_min: min_range.include_min,
+ max: max_range.max,
+ include_max: max_range.include_max
+ )
+ end
+
+ # The span covered by two ranges
+ #
+ # If self and other are contiguous, this builds a union of the two ranges.
+ # (if they aren't you are probably calling the wrong method)
+ def span(other)
+ return self if other.empty?
+
+ min_range =
+ if !min
+ self
+ elsif !other.min
+ other
+ else
+ case min <=> other.min
+ when 0
+ include_min ? self : other
+ when -1
+ self
+ when 1
+ other
+ end
+ end
+
+ max_range =
+ if !max
+ self
+ elsif !other.max
+ other
+ else
+ case max <=> other.max
+ when 0
+ include_max ? self : other
+ when -1
+ other
+ when 1
+ self
+ end
+ end
+
+ VersionRange.new(
+ min: min_range.min,
+ include_min: min_range.include_min,
+ max: max_range.max,
+ include_max: max_range.include_max
+ )
+ end
+
+ def union(other)
+ return other.union(self) if other.is_a?(VersionUnion)
+
+ if contiguous_to?(other)
+ span(other)
+ else
+ VersionUnion.union([self, other])
+ end
+ end
+
+ def contiguous_to?(other)
+ return false if other.empty?
+
+ intersects?(other) ||
+ (min == other.max && (include_min || other.include_max)) ||
+ (max == other.min && (include_max || other.include_min))
+ end
+
+ def allows_all?(other)
+ return true if other.empty?
+
+ if other.is_a?(VersionUnion)
+ return VersionUnion.new([self]).allows_all?(other)
+ end
+
+ return false if max && !other.max
+ return false if min && !other.min
+
+ if min
+ case min <=> other.min
+ when -1
+ when 0
+ return false if !include_min && other.include_min
+ when 1
+ return false
+ end
+ end
+
+ if max
+ case max <=> other.max
+ when -1
+ return false
+ when 0
+ return false if !include_max && other.include_max
+ when 1
+ end
+ end
+
+ true
+ end
+
+ def any?
+ !min && !max
+ end
+
+ def empty?
+ false
+ end
+
+ def to_s
+ @name ||= constraints.join(", ")
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def upper_invert
+ return self.class.empty unless max
+
+ VersionRange.new(min: max, include_min: !include_max)
+ end
+
+ def invert
+ return self.class.empty if any?
+
+ low = VersionRange.new(max: min, include_max: !include_min)
+ high = VersionRange.new(min: max, include_min: !include_max)
+
+ if !min
+ high
+ elsif !max
+ low
+ else
+ low.union(high)
+ end
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ min == other.min &&
+ max == other.max &&
+ include_min == other.include_min &&
+ include_max == other.include_max
+ end
+
+ private
+
+ def constraints
+ return ["any"] if any?
+ return ["= #{min}"] if min.to_s == max.to_s
+
+ c = []
+ c << "#{include_min ? ">=" : ">"} #{min}" if min
+ c << "#{include_max ? "<=" : "<"} #{max}" if max
+ c
+ end
+
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
new file mode 100644
index 0000000000..4caf6b355b
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
@@ -0,0 +1,248 @@
+require_relative 'partial_solution'
+require_relative 'term'
+require_relative 'incompatibility'
+require_relative 'solve_failure'
+
+module Bundler::PubGrub
+ class VersionSolver
+ attr_reader :logger
+ attr_reader :source
+ attr_reader :solution
+
+ def initialize(source:, root: Package.root, logger: Bundler::PubGrub.logger)
+ @logger = logger
+
+ @source = source
+
+ # { package => [incompatibility, ...]}
+ @incompatibilities = Hash.new do |h, k|
+ h[k] = []
+ end
+
+ @seen_incompatibilities = {}
+
+ @solution = PartialSolution.new
+
+ add_incompatibility Incompatibility.new([
+ Term.new(VersionConstraint.any(root), false)
+ ], cause: :root)
+
+ propagate(root)
+ end
+
+ def solved?
+ solution.unsatisfied.empty?
+ end
+
+ # Returns true if there is more work to be done, false otherwise
+ def work
+ return false if solved?
+
+ next_package = choose_package_version
+ propagate(next_package)
+
+ if solved?
+ logger.info { "Solution found after #{solution.attempted_solutions} attempts:" }
+ solution.decisions.each do |package, version|
+ next if Package.root?(package)
+ logger.info { "* #{package} #{version}" }
+ end
+
+ false
+ else
+ true
+ end
+ end
+
+ def solve
+ work until solved?
+
+ solution.decisions
+ end
+
+ alias_method :result, :solve
+
+ private
+
+ def propagate(initial_package)
+ changed = [initial_package]
+ while package = changed.shift
+ @incompatibilities[package].reverse_each do |incompatibility|
+ result = propagate_incompatibility(incompatibility)
+ if result == :conflict
+ root_cause = resolve_conflict(incompatibility)
+ changed.clear
+ changed << propagate_incompatibility(root_cause)
+ elsif result # should be a Package
+ changed << result
+ end
+ end
+ changed.uniq!
+ end
+ end
+
+ def propagate_incompatibility(incompatibility)
+ unsatisfied = nil
+ incompatibility.terms.each do |term|
+ relation = solution.relation(term)
+ if relation == :disjoint
+ return nil
+ elsif relation == :overlap
+ # If more than one term is inconclusive, we can't deduce anything
+ return nil if unsatisfied
+ unsatisfied = term
+ end
+ end
+
+ if !unsatisfied
+ return :conflict
+ end
+
+ logger.debug { "derived: #{unsatisfied.invert}" }
+
+ solution.derive(unsatisfied.invert, incompatibility)
+
+ unsatisfied.package
+ end
+
+ def next_package_to_try
+ solution.unsatisfied.min_by do |term|
+ package = term.package
+ range = term.constraint.range
+ matching_versions = source.versions_for(package, range)
+ higher_versions = source.versions_for(package, range.upper_invert)
+
+ [matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
+ end.package
+ end
+
+ def choose_package_version
+ if solution.unsatisfied.empty?
+ logger.info "No packages unsatisfied. Solving complete!"
+ return nil
+ end
+
+ package = next_package_to_try
+ unsatisfied_term = solution.unsatisfied.find { |t| t.package == package }
+ version = source.versions_for(package, unsatisfied_term.constraint.range).first
+ logger.debug { "attempting #{package} #{version}" }
+
+ if version.nil?
+ add_incompatibility source.no_versions_incompatibility_for(package, unsatisfied_term)
+ return package
+ end
+
+ conflict = false
+
+ source.incompatibilities_for(package, version).each do |incompatibility|
+ if @seen_incompatibilities.include?(incompatibility)
+ logger.debug { "knew: #{incompatibility}" }
+ next
+ end
+ @seen_incompatibilities[incompatibility] = true
+
+ add_incompatibility incompatibility
+
+ conflict ||= incompatibility.terms.all? do |term|
+ term.package == package || solution.satisfies?(term)
+ end
+ end
+
+ unless conflict
+ logger.info { "selected #{package} #{version}" }
+
+ solution.decide(package, version)
+ else
+ logger.info { "conflict: #{conflict.inspect}" }
+ end
+
+ package
+ end
+
+ def resolve_conflict(incompatibility)
+ logger.info { "conflict: #{incompatibility}" }
+
+ new_incompatibility = nil
+
+ while !incompatibility.failure?
+ most_recent_term = nil
+ most_recent_satisfier = nil
+ difference = nil
+
+ previous_level = 1
+
+ incompatibility.terms.each do |term|
+ satisfier = solution.satisfier(term)
+
+ if most_recent_satisfier.nil?
+ most_recent_term = term
+ most_recent_satisfier = satisfier
+ elsif most_recent_satisfier.index < satisfier.index
+ previous_level = [previous_level, most_recent_satisfier.decision_level].max
+ most_recent_term = term
+ most_recent_satisfier = satisfier
+ difference = nil
+ else
+ previous_level = [previous_level, satisfier.decision_level].max
+ end
+
+ if most_recent_term == term
+ difference = most_recent_satisfier.term.difference(most_recent_term)
+ if difference.empty?
+ difference = nil
+ else
+ difference_satisfier = solution.satisfier(difference.inverse)
+ previous_level = [previous_level, difference_satisfier.decision_level].max
+ end
+ end
+ end
+
+ if previous_level < most_recent_satisfier.decision_level ||
+ most_recent_satisfier.decision?
+
+ logger.info { "backtracking to #{previous_level}" }
+ solution.backtrack(previous_level)
+
+ if new_incompatibility
+ add_incompatibility(new_incompatibility)
+ end
+
+ return incompatibility
+ end
+
+ new_terms = []
+ new_terms += incompatibility.terms - [most_recent_term]
+ new_terms += most_recent_satisfier.cause.terms.reject { |term|
+ term.package == most_recent_satisfier.term.package
+ }
+ if difference
+ new_terms << difference.invert
+ end
+
+ new_incompatibility = Incompatibility.new(new_terms, cause: Incompatibility::ConflictCause.new(incompatibility, most_recent_satisfier.cause))
+
+ if incompatibility.to_s == new_incompatibility.to_s
+ logger.info { "!! failed to resolve conflicts, this shouldn't have happened" }
+ break
+ end
+
+ incompatibility = new_incompatibility
+
+ partially = difference ? " partially" : ""
+ logger.info { "! #{most_recent_term} is#{partially} satisfied by #{most_recent_satisfier.term}" }
+ logger.info { "! which is caused by #{most_recent_satisfier.cause}" }
+ logger.info { "! thus #{incompatibility}" }
+ end
+
+ raise SolveFailure.new(incompatibility)
+ end
+
+ def add_incompatibility(incompatibility)
+ logger.debug { "fact: #{incompatibility}" }
+ incompatibility.terms.each do |term|
+ package = term.package
+ @incompatibilities[package] << incompatibility
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb
new file mode 100644
index 0000000000..bbc10c3804
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+module Bundler::PubGrub
+ class VersionUnion
+ attr_reader :ranges
+
+ def self.normalize_ranges(ranges)
+ ranges = ranges.flat_map do |range|
+ range.ranges
+ end
+
+ ranges.reject!(&:empty?)
+
+ return [] if ranges.empty?
+
+ mins, ranges = ranges.partition { |r| !r.min }
+ original_ranges = mins + ranges.sort_by { |r| [r.min, r.include_min ? 0 : 1] }
+ ranges = [original_ranges.shift]
+ original_ranges.each do |range|
+ if ranges.last.contiguous_to?(range)
+ ranges << ranges.pop.span(range)
+ else
+ ranges << range
+ end
+ end
+
+ ranges
+ end
+
+ def self.union(ranges, normalize: true)
+ ranges = normalize_ranges(ranges) if normalize
+
+ if ranges.size == 0
+ VersionRange.empty
+ elsif ranges.size == 1
+ ranges[0]
+ else
+ new(ranges)
+ end
+ end
+
+ def initialize(ranges)
+ raise ArgumentError unless ranges.all? { |r| r.instance_of?(VersionRange) }
+ @ranges = ranges
+ end
+
+ def hash
+ ranges.hash
+ end
+
+ def eql?(other)
+ ranges.eql?(other.ranges)
+ end
+
+ def include?(version)
+ !!ranges.bsearch {|r| r.compare_version(version) }
+ end
+
+ def select_versions(all_versions)
+ versions = []
+ ranges.inject(all_versions) do |acc, range|
+ _, matching, higher = range.partition_versions(acc)
+ versions.concat matching
+ higher
+ end
+ versions
+ end
+
+ def intersects?(other)
+ my_ranges = ranges.dup
+ other_ranges = other.ranges.dup
+
+ my_range = my_ranges.shift
+ other_range = other_ranges.shift
+ while my_range && other_range
+ if my_range.intersects?(other_range)
+ return true
+ end
+
+ if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
+ other_range = other_ranges.shift
+ else
+ my_range = my_ranges.shift
+ end
+ end
+ end
+ alias_method :allows_any?, :intersects?
+
+ def allows_all?(other)
+ my_ranges = ranges.dup
+
+ my_range = my_ranges.shift
+
+ other.ranges.all? do |other_range|
+ while my_range
+ break if my_range.allows_all?(other_range)
+ my_range = my_ranges.shift
+ end
+
+ !!my_range
+ end
+ end
+
+ def empty?
+ false
+ end
+
+ def any?
+ false
+ end
+
+ def intersect(other)
+ my_ranges = ranges.dup
+ other_ranges = other.ranges.dup
+ new_ranges = []
+
+ my_range = my_ranges.shift
+ other_range = other_ranges.shift
+ while my_range && other_range
+ new_ranges << my_range.intersect(other_range)
+
+ if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
+ other_range = other_ranges.shift
+ else
+ my_range = my_ranges.shift
+ end
+ end
+ new_ranges.reject!(&:empty?)
+ VersionUnion.union(new_ranges, normalize: false)
+ end
+
+ def upper_invert
+ ranges.last.upper_invert
+ end
+
+ def invert
+ ranges.map(&:invert).inject(:intersect)
+ end
+
+ def union(other)
+ VersionUnion.union([self, other])
+ end
+
+ def to_s
+ output = []
+
+ ranges = self.ranges.dup
+ while !ranges.empty?
+ ne = []
+ range = ranges.shift
+ while !ranges.empty? && ranges[0].min.to_s == range.max.to_s
+ ne << range.max
+ range = range.span(ranges.shift)
+ end
+
+ ne.map! {|x| "!= #{x}" }
+ if ne.empty?
+ output << range.to_s
+ elsif range.any?
+ output << ne.join(', ')
+ else
+ output << "#{range}, #{ne.join(', ')}"
+ end
+ end
+
+ output.join(" OR ")
+ end
+
+ def inspect
+ "#<#{self.class} #{to_s}>"
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.ranges == other.ranges
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
index 8eff00bf3d..ef97d52ae7 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
@@ -425,7 +425,7 @@ class Bundler::Thor
end
def unix?
- RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
end
def truncate(string, width)
diff --git a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb b/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
deleted file mode 100644
index 70d43e0c6b..0000000000
--- a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-# frozen_string_literal: true
-#
-# tmpdir - retrieve temporary directory path
-#
-# $Id$
-#
-
-require_relative '../../fileutils/lib/fileutils'
-begin
- require 'etc.so'
-rescue LoadError # rescue LoadError for miniruby
-end
-
-class Bundler::Dir < Dir
-
- @systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
-
- ##
- # Returns the operating system's temporary file path.
-
- def self.tmpdir
- tmp = nil
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @systmpdir], ['/tmp']*2, ['.']*2].each do |name, dir = ENV[name]|
- next if !dir
- dir = File.expand_path(dir)
- stat = File.stat(dir) rescue next
- case
- when !stat.directory?
- warn "#{name} is not a directory: #{dir}"
- when !stat.writable?
- warn "#{name} is not writable: #{dir}"
- when stat.world_writable? && !stat.sticky?
- warn "#{name} is world-writable: #{dir}"
- else
- tmp = dir
- break
- end
- end
- raise ArgumentError, "could not find a temporary directory" unless tmp
- tmp
- end
-
- # Bundler::Dir.mktmpdir creates a temporary directory.
- #
- # The directory is created with 0700 permission.
- # Application should not change the permission to make the temporary directory accessible from other users.
- #
- # The prefix and suffix of the name of the directory is specified by
- # the optional first argument, <i>prefix_suffix</i>.
- # - If it is not specified or nil, "d" is used as the prefix and no suffix is used.
- # - If it is a string, it is used as the prefix and no suffix is used.
- # - If it is an array, first element is used as the prefix and second element is used as a suffix.
- #
- # Bundler::Dir.mktmpdir {|dir| dir is ".../d..." }
- # Bundler::Dir.mktmpdir("foo") {|dir| dir is ".../foo..." }
- # Bundler::Dir.mktmpdir(["foo", "bar"]) {|dir| dir is ".../foo...bar" }
- #
- # The directory is created under Bundler::Dir.tmpdir or
- # the optional second argument <i>tmpdir</i> if non-nil value is given.
- #
- # Bundler::Dir.mktmpdir {|dir| dir is "#{Bundler::Dir.tmpdir}/d..." }
- # Bundler::Dir.mktmpdir(nil, "/var/tmp") {|dir| dir is "/var/tmp/d..." }
- #
- # If a block is given,
- # it is yielded with the path of the directory.
- # The directory and its contents are removed
- # using Bundler::FileUtils.remove_entry before Bundler::Dir.mktmpdir returns.
- # The value of the block is returned.
- #
- # Bundler::Dir.mktmpdir {|dir|
- # # use the directory...
- # open("#{dir}/foo", "w") { ... }
- # }
- #
- # If a block is not given,
- # The path of the directory is returned.
- # In this case, Bundler::Dir.mktmpdir doesn't remove the directory.
- #
- # dir = Bundler::Dir.mktmpdir
- # begin
- # # use the directory...
- # open("#{dir}/foo", "w") { ... }
- # ensure
- # # remove the directory.
- # Bundler::FileUtils.remove_entry dir
- # end
- #
- def self.mktmpdir(prefix_suffix=nil, *rest, **options)
- base = nil
- path = Tmpname.create(prefix_suffix || "d", *rest, **options) {|p, _, _, d|
- base = d
- mkdir(p, 0700)
- }
- if block_given?
- begin
- yield path.dup
- ensure
- unless base
- stat = File.stat(File.dirname(path))
- if stat.world_writable? and !stat.sticky?
- raise ArgumentError, "parent directory is world writable but not sticky"
- end
- end
- Bundler::FileUtils.remove_entry path
- end
- else
- path
- end
- end
-
- module Tmpname # :nodoc:
- module_function
-
- def tmpdir
- Bundler::Dir.tmpdir
- end
-
- UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
-
- class << (RANDOM = Random.new)
- MAX = 36**6 # < 0x100000000
- def next
- rand(MAX).to_s(36)
- end
- end
- private_constant :RANDOM
-
- def create(basename, tmpdir=nil, max_try: nil, **opts)
- origdir = tmpdir
- tmpdir ||= tmpdir()
- n = nil
- prefix, suffix = basename
- prefix = (String.try_convert(prefix) or
- raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
- prefix = prefix.delete(UNUSABLE_CHARS)
- suffix &&= (String.try_convert(suffix) or
- raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
- suffix &&= suffix.delete(UNUSABLE_CHARS)
- begin
- t = Time.now.strftime("%Y%m%d")
- path = "#{prefix}#{t}-#{$$}-#{RANDOM.next}"\
- "#{n ? %[-#{n}] : ''}#{suffix||''}"
- path = File.join(tmpdir, path)
- yield(path, n, opts, origdir)
- rescue Errno::EEXIST
- n ||= 0
- n += 1
- retry if !max_try or n < max_try
- raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
- end
- path
- end
- end
-end
diff --git a/lib/bundler/vendor/uri/lib/uri.rb b/lib/bundler/vendor/uri/lib/uri.rb
index 0e574dd2b1..976320f6bd 100644
--- a/lib/bundler/vendor/uri/lib/uri.rb
+++ b/lib/bundler/vendor/uri/lib/uri.rb
@@ -30,7 +30,7 @@
# class RSYNC < Generic
# DEFAULT_PORT = 873
# end
-# @@schemes['RSYNC'] = RSYNC
+# register_scheme 'RSYNC', RSYNC
# end
# #=> Bundler::URI::RSYNC
#
@@ -70,7 +70,6 @@
# - Bundler::URI::REGEXP - (in uri/common.rb)
# - Bundler::URI::REGEXP::PATTERN - (in uri/common.rb)
# - Bundler::URI::Util - (in uri/common.rb)
-# - Bundler::URI::Escape - (in uri/common.rb)
# - Bundler::URI::Error - (in uri/common.rb)
# - Bundler::URI::InvalidURIError - (in uri/common.rb)
# - Bundler::URI::InvalidComponentError - (in uri/common.rb)
@@ -101,3 +100,5 @@ require_relative 'uri/https'
require_relative 'uri/ldap'
require_relative 'uri/ldaps'
require_relative 'uri/mailto'
+require_relative 'uri/ws'
+require_relative 'uri/wss'
diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb
index 6539e1810f..914a4c7581 100644
--- a/lib/bundler/vendor/uri/lib/uri/common.rb
+++ b/lib/bundler/vendor/uri/lib/uri/common.rb
@@ -13,9 +13,12 @@ require_relative "rfc2396_parser"
require_relative "rfc3986_parser"
module Bundler::URI
+ include RFC2396_REGEXP
+
REGEXP = RFC2396_REGEXP
Parser = RFC2396_Parser
RFC3986_PARSER = RFC3986_Parser.new
+ Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
# Bundler::URI::Parser.new
DEFAULT_PARSER = Parser.new
@@ -27,6 +30,7 @@ module Bundler::URI
DEFAULT_PARSER.regexp.each_pair do |sym, str|
const_set(sym, str)
end
+ Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)
module Util # :nodoc:
def make_components_hash(klass, array_hash)
@@ -60,24 +64,42 @@ module Bundler::URI
module_function :make_components_hash
end
- include REGEXP
+ module Schemes
+ end
+ private_constant :Schemes
+
+ #
+ # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+.
+ # Note that currently only schemes which after .upcase are valid constant names
+ # can be registered (no -/+/. allowed).
+ #
+ def self.register_scheme(scheme, klass)
+ Schemes.const_set(scheme.to_s.upcase, klass)
+ end
- @@schemes = {}
# Returns a Hash of the defined schemes.
def self.scheme_list
- @@schemes
+ Schemes.constants.map { |name|
+ [name.to_s.upcase, Schemes.const_get(name)]
+ }.to_h
end
+ INITIAL_SCHEMES = scheme_list
+ private_constant :INITIAL_SCHEMES
+ Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
+
#
# Construct a Bundler::URI instance, using the scheme to detect the appropriate class
# from +Bundler::URI.scheme_list+.
#
def self.for(scheme, *arguments, default: Generic)
- if scheme
- uri_class = @@schemes[scheme.upcase] || default
- else
- uri_class = default
+ const_name = scheme.to_s.upcase
+
+ uri_class = INITIAL_SCHEMES[const_name]
+ uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
+ Schemes.const_get(const_name, false)
end
+ uri_class ||= default
return uri_class.new(scheme, *arguments)
end
@@ -278,6 +300,7 @@ module Bundler::URI
256.times do |i|
TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
end
+ TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze
TBLENCWWWCOMP_[' '] = '+'
TBLENCWWWCOMP_.freeze
TBLDECWWWCOMP_ = {} # :nodoc:
@@ -303,6 +326,33 @@ module Bundler::URI
#
# See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form.
def self.encode_www_form_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
+ end
+
+ # Decodes given +str+ of URL-encoded form data.
+ #
+ # This decodes + to SP.
+ #
+ # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
+ def self.decode_www_form_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/\+|%\h\h/, str, enc)
+ end
+
+ # Encodes +str+ using URL encoding
+ #
+ # This encodes SP to %20 instead of +.
+ def self.encode_uri_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
+ end
+
+ # Decodes given +str+ of URL-encoded data.
+ #
+ # This does not decode + to SP.
+ def self.decode_uri_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/%\h\h/, str, enc)
+ end
+
+ def self._encode_uri_component(regexp, table, str, enc)
str = str.to_s.dup
if str.encoding != Encoding::ASCII_8BIT
if enc && enc != Encoding::ASCII_8BIT
@@ -311,19 +361,16 @@ module Bundler::URI
end
str.force_encoding(Encoding::ASCII_8BIT)
end
- str.gsub!(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_)
+ str.gsub!(regexp, table)
str.force_encoding(Encoding::US_ASCII)
end
+ private_class_method :_encode_uri_component
- # Decodes given +str+ of URL-encoded form data.
- #
- # This decodes + to SP.
- #
- # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
- def self.decode_www_form_component(str, enc=Encoding::UTF_8)
- raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/ =~ str
- str.b.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
+ def self._decode_uri_component(regexp, str, enc)
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/.match?(str)
+ str.b.gsub(regexp, TBLDECWWWCOMP_).force_encoding(enc)
end
+ private_class_method :_decode_uri_component
# Generates URL-encoded form data from given +enum+.
#
@@ -653,6 +700,7 @@ module Bundler::URI
"utf-16"=>"utf-16le",
"utf-16le"=>"utf-16le",
} # :nodoc:
+ Ractor.make_shareable(WEB_ENCODINGS_) if defined?(Ractor)
# :nodoc:
# return encoding or nil
diff --git a/lib/bundler/vendor/uri/lib/uri/file.rb b/lib/bundler/vendor/uri/lib/uri/file.rb
index df42f8bcdd..8d75a9de7a 100644
--- a/lib/bundler/vendor/uri/lib/uri/file.rb
+++ b/lib/bundler/vendor/uri/lib/uri/file.rb
@@ -33,6 +33,9 @@ module Bundler::URI
# If an Array is used, the components must be passed in the
# order <code>[host, path]</code>.
#
+ # A path from e.g. the File class should be escaped before
+ # being passed.
+ #
# Examples:
#
# require 'bundler/vendor/uri/lib/uri'
@@ -44,6 +47,9 @@ module Bundler::URI
# :path => '/ruby/src'})
# uri2.to_s # => "file://host.example.com/ruby/src"
#
+ # uri3 = Bundler::URI::File.build({:path => Bundler::URI::escape('/path/my file.txt')})
+ # uri3.to_s # => "file:///path/my%20file.txt"
+ #
def self.build(args)
tmp = Util::make_components_hash(self, args)
super(tmp)
@@ -90,5 +96,5 @@ module Bundler::URI
end
end
- @@schemes['FILE'] = File
+ register_scheme 'FILE', File
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ftp.rb b/lib/bundler/vendor/uri/lib/uri/ftp.rb
index 2252e405d5..48b4c6718d 100644
--- a/lib/bundler/vendor/uri/lib/uri/ftp.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ftp.rb
@@ -262,5 +262,6 @@ module Bundler::URI
return str
end
end
- @@schemes['FTP'] = FTP
+
+ register_scheme 'FTP', FTP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/generic.rb b/lib/bundler/vendor/uri/lib/uri/generic.rb
index f29ba6cf18..9ae6915826 100644
--- a/lib/bundler/vendor/uri/lib/uri/generic.rb
+++ b/lib/bundler/vendor/uri/lib/uri/generic.rb
@@ -564,16 +564,26 @@ module Bundler::URI
end
end
- # Returns the user component.
+ # Returns the user component (without Bundler::URI decoding).
def user
@user
end
- # Returns the password component.
+ # Returns the password component (without Bundler::URI decoding).
def password
@password
end
+ # Returns the user component after Bundler::URI decoding.
+ def decoded_user
+ Bundler::URI.decode_uri_component(@user) if @user
+ end
+
+ # Returns the password component after Bundler::URI decoding.
+ def decoded_password
+ Bundler::URI.decode_uri_component(@password) if @password
+ end
+
#
# Checks the host +v+ component for RFC2396 compliance
# and against the Bundler::URI::Parser Regexp for :HOST.
@@ -643,7 +653,7 @@ module Bundler::URI
#
def hostname
v = self.host
- /\A\[(.*)\]\z/ =~ v ? $1 : v
+ v&.start_with?('[') && v.end_with?(']') ? v[1..-2] : v
end
# Sets the host part of the Bundler::URI as the argument with brackets for IPv6 addresses.
@@ -659,7 +669,7 @@ module Bundler::URI
# it is wrapped with brackets.
#
def hostname=(v)
- v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
+ v = "[#{v}]" if !(v&.start_with?('[') && v&.end_with?(']')) && v&.index(':')
self.host = v
end
@@ -1514,9 +1524,19 @@ module Bundler::URI
proxy_uri = env["CGI_#{name.upcase}"]
end
elsif name == 'http_proxy'
- unless proxy_uri = env[name]
- if proxy_uri = env[name.upcase]
- warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ if RUBY_ENGINE == 'jruby' && p_addr = ENV_JAVA['http.proxyHost']
+ p_port = ENV_JAVA['http.proxyPort']
+ if p_user = ENV_JAVA['http.proxyUser']
+ p_pass = ENV_JAVA['http.proxyPass']
+ proxy_uri = "http://#{p_user}:#{p_pass}@#{p_addr}:#{p_port}"
+ else
+ proxy_uri = "http://#{p_addr}:#{p_port}"
+ end
+ else
+ unless proxy_uri = env[name]
+ if proxy_uri = env[name.upcase]
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ end
end
end
else
diff --git a/lib/bundler/vendor/uri/lib/uri/http.rb b/lib/bundler/vendor/uri/lib/uri/http.rb
index 50d7e427a5..2c44810644 100644
--- a/lib/bundler/vendor/uri/lib/uri/http.rb
+++ b/lib/bundler/vendor/uri/lib/uri/http.rb
@@ -80,8 +80,46 @@ module Bundler::URI
url = @query ? "#@path?#@query" : @path.dup
url.start_with?(?/.freeze) ? url : ?/ + url
end
- end
- @@schemes['HTTP'] = HTTP
+ #
+ # == Description
+ #
+ # Returns the authority for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.
+ #
+ #
+ # Example:
+ #
+ # Bundler::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').authority #=> "www.example.com"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').authority #=> "www.example.com:8000"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').authority #=> "www.example.com"
+ #
+ def authority
+ if port == default_port
+ host
+ else
+ "#{host}:#{port}"
+ end
+ end
+
+ #
+ # == Description
+ #
+ # Returns the origin for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc6454.
+ #
+ #
+ # Example:
+ #
+ # Bundler::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').origin #=> "http://www.example.com"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').origin #=> "http://www.example.com:8000"
+ # Bundler::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').origin #=> "http://www.example.com"
+ # Bundler::URI::HTTPS.build(host: 'www.example.com', path: '/foo/bar').origin #=> "https://www.example.com"
+ #
+ def origin
+ "#{scheme}://#{authority}"
+ end
+ end
+ register_scheme 'HTTP', HTTP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/https.rb b/lib/bundler/vendor/uri/lib/uri/https.rb
index 4fd4e9af7b..e4556e3ecb 100644
--- a/lib/bundler/vendor/uri/lib/uri/https.rb
+++ b/lib/bundler/vendor/uri/lib/uri/https.rb
@@ -18,5 +18,6 @@ module Bundler::URI
# A Default port of 443 for Bundler::URI::HTTPS
DEFAULT_PORT = 443
end
- @@schemes['HTTPS'] = HTTPS
+
+ register_scheme 'HTTPS', HTTPS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ldap.rb b/lib/bundler/vendor/uri/lib/uri/ldap.rb
index 6e9e1918f6..9811b6e2f5 100644
--- a/lib/bundler/vendor/uri/lib/uri/ldap.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ldap.rb
@@ -257,5 +257,5 @@ module Bundler::URI
end
end
- @@schemes['LDAP'] = LDAP
+ register_scheme 'LDAP', LDAP
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ldaps.rb b/lib/bundler/vendor/uri/lib/uri/ldaps.rb
index 0af35bb16b..c786168450 100644
--- a/lib/bundler/vendor/uri/lib/uri/ldaps.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ldaps.rb
@@ -17,5 +17,6 @@ module Bundler::URI
# A Default port of 636 for Bundler::URI::LDAPS
DEFAULT_PORT = 636
end
- @@schemes['LDAPS'] = LDAPS
+
+ register_scheme 'LDAPS', LDAPS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/mailto.rb b/lib/bundler/vendor/uri/lib/uri/mailto.rb
index ff7ab7e114..ff2e30be86 100644
--- a/lib/bundler/vendor/uri/lib/uri/mailto.rb
+++ b/lib/bundler/vendor/uri/lib/uri/mailto.rb
@@ -15,7 +15,7 @@ module Bundler::URI
# RFC6068, the mailto URL scheme.
#
class MailTo < Generic
- include REGEXP
+ include RFC2396_REGEXP
# A Default port of nil for Bundler::URI::MailTo.
DEFAULT_PORT = nil
@@ -289,5 +289,5 @@ module Bundler::URI
alias to_rfc822text to_mailtext
end
- @@schemes['MAILTO'] = MailTo
+ register_scheme 'MAILTO', MailTo
end
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
index e48e164f4c..09c22c9906 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
@@ -116,7 +116,7 @@ module Bundler::URI
# See also Bundler::URI::Parser.initialize_regexp.
attr_reader :regexp
- # Returns a split Bundler::URI against regexp[:ABS_URI].
+ # Returns a split Bundler::URI against +regexp[:ABS_URI]+.
def split(uri)
case uri
when ''
@@ -257,8 +257,8 @@ module Bundler::URI
end
end
- # Returns Regexp that is default self.regexp[:ABS_URI_REF],
- # unless +schemes+ is provided. Then it is a Regexp.union with self.pattern[:X_ABS_URI].
+ # Returns Regexp that is default +self.regexp[:ABS_URI_REF]+,
+ # unless +schemes+ is provided. Then it is a Regexp.union with +self.pattern[:X_ABS_URI]+.
def make_regexp(schemes = nil)
unless schemes
@regexp[:ABS_URI_REF]
@@ -277,7 +277,7 @@ module Bundler::URI
# +str+::
# String to make safe
# +unsafe+::
- # Regexp to apply. Defaults to self.regexp[:UNSAFE]
+ # Regexp to apply. Defaults to +self.regexp[:UNSAFE]+
#
# == Description
#
@@ -309,7 +309,7 @@ module Bundler::URI
# +str+::
# String to remove escapes from
# +escaped+::
- # Regexp to apply. Defaults to self.regexp[:ESCAPED]
+ # Regexp to apply. Defaults to +self.regexp[:ESCAPED]+
#
# == Description
#
@@ -322,8 +322,14 @@ module Bundler::URI
end
@@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind_call(self)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
end
private
@@ -491,8 +497,8 @@ module Bundler::URI
ret = {}
# for Bundler::URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for Bundler::URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
index 2029cfd056..a85511c146 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
@@ -2,9 +2,8 @@
module Bundler::URI
class RFC3986_Parser # :nodoc:
# Bundler::URI defined in RFC3986
- # this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -79,8 +78,14 @@ module Bundler::URI
end
@@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind_call(self)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
end
private
@@ -95,7 +100,7 @@ module Bundler::URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb
index f2bb0ebad2..84b08eee30 100644
--- a/lib/bundler/vendor/uri/lib/uri/version.rb
+++ b/lib/bundler/vendor/uri/lib/uri/version.rb
@@ -1,6 +1,6 @@
module Bundler::URI
# :stopdoc:
- VERSION_CODE = '001001'.freeze
+ VERSION_CODE = '001202'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/bundler/vendor/uri/lib/uri/ws.rb b/lib/bundler/vendor/uri/lib/uri/ws.rb
index 58e08bf98e..10ae6f5834 100644
--- a/lib/bundler/vendor/uri/lib/uri/ws.rb
+++ b/lib/bundler/vendor/uri/lib/uri/ws.rb
@@ -79,6 +79,5 @@ module Bundler::URI
end
end
- @@schemes['WS'] = WS
-
+ register_scheme 'WS', WS
end
diff --git a/lib/bundler/vendor/uri/lib/uri/wss.rb b/lib/bundler/vendor/uri/lib/uri/wss.rb
index 3827053c7b..e8db1ceabf 100644
--- a/lib/bundler/vendor/uri/lib/uri/wss.rb
+++ b/lib/bundler/vendor/uri/lib/uri/wss.rb
@@ -18,5 +18,6 @@ module Bundler::URI
# A Default port of 443 for Bundler::URI::WSS
DEFAULT_PORT = 443
end
- @@schemes['WSS'] = WSS
+
+ register_scheme 'WSS', WSS
end
diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb
deleted file mode 100644
index d1976f5cb4..0000000000
--- a/lib/bundler/vendored_molinillo.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler; end
-require_relative "vendor/molinillo/lib/molinillo"
diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb
index dc9573e025..e29f27cdfd 100644
--- a/lib/bundler/vendored_persistent.rb
+++ b/lib/bundler/vendored_persistent.rb
@@ -11,37 +11,5 @@ end
require_relative "vendor/net-http-persistent/lib/net/http/persistent"
module Bundler
- class PersistentHTTP < Persistent::Net::HTTP::Persistent
- def connection_for(uri)
- super(uri) do |connection|
- result = yield connection
- warn_old_tls_version_rubygems_connection(uri, connection)
- result
- end
- end
-
- def warn_old_tls_version_rubygems_connection(uri, connection)
- return unless connection.http.use_ssl?
- return unless (uri.host || "").end_with?("rubygems.org")
-
- socket = connection.instance_variable_get(:@socket)
- return unless socket
- socket_io = socket.io
- return unless socket_io.respond_to?(:ssl_version)
- ssl_version = socket_io.ssl_version
-
- case ssl_version
- when /TLSv([\d\.]+)/
- version = Gem::Version.new($1)
- if version < Gem::Version.new("1.2")
- Bundler.ui.warn \
- "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
- "Starting in January 2018, RubyGems.org will refuse connection requests from these " \
- "very old versions of OpenSSL. If you will need to continue installing gems after " \
- "January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
- :wrap => true
- end
- end
- end
- end
+ PersistentHTTP = Persistent::Net::HTTP::Persistent
end
diff --git a/lib/bundler/vendored_tmpdir.rb b/lib/bundler/vendored_pub_grub.rb
index 43b4fa75fe..b36a996b29 100644
--- a/lib/bundler/vendored_tmpdir.rb
+++ b/lib/bundler/vendored_pub_grub.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Bundler; end
-require_relative "vendor/tmpdir/lib/tmpdir"
+require_relative "vendor/pub_grub/lib/pub_grub"
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 1acb00fd3a..8ef7be935b 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.4.0.dev".freeze
+ VERSION = "2.4.19".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
end
+
+ def self.gem_version
+ @gem_version ||= Gem::Version.create(VERSION)
+ end
end
diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb
deleted file mode 100644
index 12a956d6a0..0000000000
--- a/lib/bundler/version_ranges.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-# frozen_string_literal: true
-
-module Bundler
- module VersionRanges
- NEq = Struct.new(:version)
- ReqR = Struct.new(:left, :right)
- class ReqR
- Endpoint = Struct.new(:version, :inclusive) do
- def <=>(other)
- if version.equal?(INFINITY)
- return 0 if other.version.equal?(INFINITY)
- return 1
- elsif other.version.equal?(INFINITY)
- return -1
- end
-
- comp = version <=> other.version
- return comp unless comp.zero?
-
- if inclusive && !other.inclusive
- 1
- elsif !inclusive && other.inclusive
- -1
- else
- 0
- end
- end
- end
-
- def to_s
- "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}"
- end
- INFINITY = begin
- inf = Object.new
- def inf.to_s
- "∞"
- end
- def inf.<=>(other)
- return 0 if other.equal?(self)
- 1
- end
- inf.freeze
- end
- ZERO = Gem::Version.new("0.a")
-
- def cover?(v)
- return false if left.inclusive && left.version > v
- return false if !left.inclusive && left.version >= v
-
- if right.version != INFINITY
- return false if right.inclusive && right.version < v
- return false if !right.inclusive && right.version <= v
- end
-
- true
- end
-
- def empty?
- left.version == right.version && !(left.inclusive && right.inclusive)
- end
-
- def single?
- left.version == right.version
- end
-
- def <=>(other)
- return -1 if other.equal?(INFINITY)
-
- comp = left <=> other.left
- return comp unless comp.zero?
-
- right <=> other.right
- end
-
- UNIVERSAL = ReqR.new(ReqR::Endpoint.new(Gem::Version.new("0.a"), true), ReqR::Endpoint.new(ReqR::INFINITY, false)).freeze
- end
-
- def self.for_many(requirements)
- requirements = requirements.map(&:requirements).flatten(1).map {|r| r.join(" ") }
- requirements << ">= 0.a" if requirements.empty?
- requirement = Gem::Requirement.new(requirements)
- self.for(requirement)
- end
-
- def self.for(requirement)
- ranges = requirement.requirements.map do |op, v|
- case op
- when "=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v, true))
- when "!=" then NEq.new(v)
- when ">=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(ReqR::INFINITY, false))
- when ">" then ReqR.new(ReqR::Endpoint.new(v, false), ReqR::Endpoint.new(ReqR::INFINITY, false))
- when "<" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, false))
- when "<=" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, true))
- when "~>" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v.bump, false))
- else raise "unknown version op #{op} in requirement #{requirement}"
- end
- end.uniq
- ranges, neqs = ranges.partition {|r| !r.is_a?(NEq) }
-
- [ranges.sort, neqs.map(&:version)]
- end
-
- def self.empty?(ranges, neqs)
- !ranges.reduce(ReqR::UNIVERSAL) do |last_range, curr_range|
- next false unless last_range
- next false if curr_range.single? && neqs.include?(curr_range.left.version)
- next curr_range if last_range.right.version == ReqR::INFINITY
- case last_range.right.version <=> curr_range.left.version
- # higher
- when 1 then next ReqR.new(curr_range.left, last_range.right)
- # equal
- when 0
- if last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version)
- ReqR.new(curr_range.left, [curr_range.right, last_range.right].max)
- end
- # lower
- when -1 then next false
- end
- end
- end
- end
-end
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
index 5e4ee21c51..3ebd6f01db 100644
--- a/lib/bundler/worker.rb
+++ b/lib/bundler/worker.rb
@@ -87,14 +87,12 @@ module Bundler
creation_errors = []
@threads = Array.new(@size) do |i|
- begin
- Thread.start { process_queue(i) }.tap do |thread|
- thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
- end
- rescue ThreadError => e
- creation_errors << e
- nil
+ Thread.start { process_queue(i) }.tap do |thread|
+ thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=)
end
+ rescue ThreadError => e
+ creation_errors << e
+ nil
end.compact
add_interrupt_handler unless @threads.empty?
diff --git a/lib/cgi.rb b/lib/cgi.rb
index affe8fd3fc..7dc3a64941 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -288,7 +288,7 @@
#
class CGI
- VERSION = "0.3.2"
+ VERSION = "0.3.7"
end
require 'cgi/core'
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 6b0d89ca3b..1c4ef6a600 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -40,6 +40,10 @@ class CGI
class Cookie < Array
@@accept_charset="UTF-8" unless defined?(@@accept_charset)
+ TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
+ PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
+ DOMAIN_VALUE_RE = %r"\A\.?(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
+
# Create a new CGI::Cookie object.
#
# :call-seq:
@@ -72,8 +76,8 @@ class CGI
@domain = nil
@expires = nil
if name.kind_of?(String)
- @name = name
- @path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.name = name
+ self.path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
@secure = false
@httponly = false
return super(value)
@@ -84,11 +88,11 @@ class CGI
raise ArgumentError, "`name' required"
end
- @name = options["name"]
+ self.name = options["name"]
value = Array(options["value"])
# simple support for IE
- @path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
- @domain = options["domain"]
+ self.path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.domain = options["domain"]
@expires = options["expires"]
@secure = options["secure"] == true
@httponly = options["httponly"] == true
@@ -97,11 +101,35 @@ class CGI
end
# Name of this cookie, as a +String+
- attr_accessor :name
+ attr_reader :name
+ # Set name of this cookie
+ def name=(str)
+ if str and !TOKEN_RE.match?(str)
+ raise ArgumentError, "invalid name: #{str.dump}"
+ end
+ @name = str
+ end
+
# Path for which this cookie applies, as a +String+
- attr_accessor :path
+ attr_reader :path
+ # Set path for which this cookie applies
+ def path=(str)
+ if str and !PATH_VALUE_RE.match?(str)
+ raise ArgumentError, "invalid path: #{str.dump}"
+ end
+ @path = str
+ end
+
# Domain for which this cookie applies, as a +String+
- attr_accessor :domain
+ attr_reader :domain
+ # Set domain for which this cookie applies
+ def domain=(str)
+ if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
+ raise ArgumentError, "invalid domain: #{str.dump}"
+ end
+ @domain = str
+ end
+
# Time at which this cookie expires, as a +Time+
attr_accessor :expires
# True if this cookie is secure; false otherwise
@@ -162,9 +190,10 @@ class CGI
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
- values = cookies[name].value + values
+ cookies[name].concat(values)
+ else
+ cookies[name] = Cookie.new(name, *values)
end
- cookies[name] = Cookie.new(name, *values)
end
cookies
diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index bec76e0749..62e606837a 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -188,17 +188,28 @@ class CGI
# Using #header with the HTML5 tag maker will create a <header> element.
alias :header :http_header
+ def _no_crlf_check(str)
+ if str
+ str = str.to_s
+ raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
+ str
+ else
+ nil
+ end
+ end
+ private :_no_crlf_check
+
def _header_for_string(content_type) #:nodoc:
buf = ''.dup
if nph?()
- buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
+ buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
- buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
+ buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}"
buf << "Connection: close#{EOL}"
end
- buf << "Content-Type: #{content_type}#{EOL}"
+ buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}"
if @output_cookies
- @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
+ @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" }
end
return buf
end # _header_for_string
@@ -213,9 +224,9 @@ class CGI
## NPH
options.delete('nph') if defined?(MOD_RUBY)
if options.delete('nph') || nph?()
- protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
+ protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'
status = options.delete('status')
- status = HTTP_STATUS[status] || status || '200 OK'
+ status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK'
buf << "#{protocol} #{status}#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
@@ -223,38 +234,38 @@ class CGI
end
## common headers
status = options.delete('status')
- buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
+ buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status
server = options.delete('server')
- buf << "Server: #{server}#{EOL}" if server
+ buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server
connection = options.delete('connection')
- buf << "Connection: #{connection}#{EOL}" if connection
+ buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection
type = options.delete('type')
- buf << "Content-Type: #{type}#{EOL}" #if type
+ buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type
length = options.delete('length')
- buf << "Content-Length: #{length}#{EOL}" if length
+ buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length
language = options.delete('language')
- buf << "Content-Language: #{language}#{EOL}" if language
+ buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language
expires = options.delete('expires')
buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
## cookie
if cookie = options.delete('cookie')
case cookie
when String, Cookie
- buf << "Set-Cookie: #{cookie}#{EOL}"
+ buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}"
when Array
arr = cookie
- arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
when Hash
hash = cookie
- hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
end
if @output_cookies
- @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
## other headers
options.each do |key, value|
- buf << "#{key}: #{value}#{EOL}"
+ buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}"
end
return buf
end # _header_for_hash
diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb
index 5a5c77ac97..ce77a0ccd5 100644
--- a/lib/cgi/util.rb
+++ b/lib/cgi/util.rb
@@ -178,7 +178,7 @@ module CGI::Util
def escapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do
+ string.gsub(/<\/?(?:#{elements.join("|")})\b[^<>]*+>?/im) do
CGI.escapeHTML($&)
end
else
@@ -198,7 +198,7 @@ module CGI::Util
def unescapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/i) do
+ string.gsub(/&lt;\/?(?:#{elements.join("|")})\b(?>[^&]+|&(?![gl]t;)\w+;)*(?:&gt;)?/im) do
unescapeHTML($&)
end
else
diff --git a/lib/csv.rb b/lib/csv.rb
index 06a490f34c..0307033941 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -48,7 +48,7 @@
#
# === Interface
#
-# * CSV now uses Hash-style parameters to set options.
+# * CSV now uses keyword parameters to set options.
# * CSV no longer has generate_row() or parse_row().
# * The old CSV's Reader and Writer classes have been dropped.
# * CSV::open() is now more like Ruby's open().
@@ -95,16 +95,24 @@ require "stringio"
require_relative "csv/fields_converter"
require_relative "csv/input_record_separator"
-require_relative "csv/match_p"
require_relative "csv/parser"
require_relative "csv/row"
require_relative "csv/table"
require_relative "csv/writer"
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
# == \CSV
-# \CSV (comma-separated variables) data is a text representation of a table:
+#
+# === In a Hurry?
+#
+# If you are familiar with \CSV data and have a particular task in mind,
+# you may want to go directly to the:
+# - {Recipes for CSV}[doc/csv/recipes/recipes_rdoc.html].
+#
+# Otherwise, read on here, about the API: classes, methods, and constants.
+#
+# === \CSV Data
+#
+# \CSV (comma-separated values) data is a text representation of a table:
# - A _row_ _separator_ delimits table rows.
# A common row separator is the newline character <tt>"\n"</tt>.
# - A _column_ _separator_ delimits fields in a row.
@@ -346,7 +354,9 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
# - +row_sep+: Specifies the row separator; used to delimit rows.
# - +col_sep+: Specifies the column separator; used to delimit fields.
# - +quote_char+: Specifies the quote character; used to quote fields.
-# - +field_size_limit+: Specifies the maximum field size allowed.
+# - +field_size_limit+: Specifies the maximum field size + 1 allowed.
+# Deprecated since 3.2.3. Use +max_field_size+ instead.
+# - +max_field_size+: Specifies the maximum field size allowed.
# - +converters+: Specifies the field converters to be used.
# - +unconverted_fields+: Specifies whether unconverted fields are to be available.
# - +headers+: Specifies whether data contains headers,
@@ -703,7 +713,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
# Header converters operate only on headers (and not on other rows).
#
# There are three ways to use header \converters;
-# these examples use built-in header converter +:dowhcase+,
+# these examples use built-in header converter +:downcase+,
# which downcases each parsed header.
#
# - Option +header_converters+ with a singleton parsing method:
@@ -853,8 +863,9 @@ class CSV
# <b><tt>index</tt></b>:: The zero-based index of the field in its row.
# <b><tt>line</tt></b>:: The line of the data source this row is from.
# <b><tt>header</tt></b>:: The header for the column, when available.
+ # <b><tt>quoted?</tt></b>:: True or false, whether the original value is quoted or not.
#
- FieldInfo = Struct.new(:index, :line, :header)
+ FieldInfo = Struct.new(:index, :line, :header, :quoted?)
# A Regexp used to find and convert some common Date formats.
DateMatcher = / \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} |
@@ -862,10 +873,9 @@ class CSV
# A Regexp used to find and convert some common DateTime formats.
DateTimeMatcher =
/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
- \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} |
- # ISO-8601
+ # ISO-8601 and RFC-3339 (space instead of T) recognized by DateTime.parse
\d{4}-\d{2}-\d{2}
- (?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
+ (?:[T\s]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
)\z /x
# The encoding used by all converters.
@@ -915,7 +925,8 @@ class CSV
symbol: lambda { |h|
h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
gsub(/\s+/, "_").to_sym
- }
+ },
+ symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
}
# Default values for method options.
@@ -926,6 +937,7 @@ class CSV
quote_char: '"',
# For parsing.
field_size_limit: nil,
+ max_field_size: nil,
converters: nil,
unconverted_fields: nil,
headers: false,
@@ -993,7 +1005,7 @@ class CSV
def instance(data = $stdout, **options)
# create a _signature_ for this method call, data object and options
sig = [data.object_id] +
- options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
+ options.values_at(*DEFAULT_OPTIONS.keys)
# fetch or create the instance for this signature
@@instances ||= Hash.new
@@ -1007,65 +1019,190 @@ class CSV
end
# :call-seq:
- # filter(**options) {|row| ... }
- # filter(in_string, **options) {|row| ... }
- # filter(in_io, **options) {|row| ... }
- # filter(in_string, out_string, **options) {|row| ... }
- # filter(in_string, out_io, **options) {|row| ... }
- # filter(in_io, out_string, **options) {|row| ... }
- # filter(in_io, out_io, **options) {|row| ... }
- #
- # Reads \CSV input and writes \CSV output.
- #
- # For each input row:
- # - Forms the data into:
- # - A CSV::Row object, if headers are in use.
- # - An \Array of Arrays, otherwise.
- # - Calls the block with that object.
- # - Appends the block's return value to the output.
+ # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
+ # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
+ # filter(**options) {|row| ... } -> array_of_arrays or csv_table
#
- # Arguments:
- # * \CSV source:
- # * Argument +in_string+, if given, should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # * Argument +in_io+, if given, should be an IO object that is
- # open for reading; on return, the IO object will be closed.
- # * If neither +in_string+ nor +in_io+ is given,
- # the input stream defaults to {ARGF}[https://ruby-doc.org/core/ARGF.html].
- # * \CSV output:
- # * Argument +out_string+, if given, should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # * Argument +out_io+, if given, should be an IO object that is
- # ppen for writing; on return, the IO object will be closed.
- # * If neither +out_string+ nor +out_io+ is given,
- # the output stream defaults to <tt>$stdout</tt>.
- # * Argument +options+ should be keyword arguments.
- # - Each argument name that is prefixed with +in_+ or +input_+
- # is stripped of its prefix and is treated as an option
- # for parsing the input.
- # Option +input_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
- # - Each argument name that is prefixed with +out_+ or +output_+
- # is stripped of its prefix and is treated as an option
- # for generating the output.
- # Option +output_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
- # - Each argument not prefixed as above is treated as an option
- # both for parsing the input and for generating the output.
- # - See {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
- # and {Options for Generating}[#class-CSV-label-Options+for+Generating].
+ # - Parses \CSV from a source (\String, \IO stream, or ARGF).
+ # - Calls the given block with each parsed row:
+ # - Without headers, each row is an \Array.
+ # - With headers, each row is a CSV::Row.
+ # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
+ # - Returns the parsed source:
+ # - Without headers, an \Array of \Arrays.
+ # - With headers, a CSV::Table.
#
- # Example:
- # in_string = "foo,0\nbar,1\nbaz,2\n"
+ # When +in_string_or_io+ is given, but not +out_string_or_io+,
+ # parses from the given +in_string_or_io+
+ # and generates to STDOUT.
+ #
+ # \String input without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
+ # CSV.filter(in_string) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \String input with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # CSV.filter(in_string, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \IO stream input without headers:
+ #
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
+ # File.open('t.csv') do |in_io|
+ # CSV.filter(in_io) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # \IO stream input with headers:
+ #
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
+ # File.open('t.csv') do |in_io|
+ # CSV.filter(in_io, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # When both +in_string_or_io+ and +out_string_or_io+ are given,
+ # parses from +in_string_or_io+ and generates to +out_string_or_io+.
+ #
+ # \String output without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
# out_string = ''
# CSV.filter(in_string, out_string) do |row|
- # row[0] = row[0].upcase
- # row[1] *= 4
- # end
- # out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \String output with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # out_string = ''
+ # CSV.filter(in_string, out_string, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \IO stream output without headers:
+ #
+ # in_string = "foo,0\nbar,1\nbaz,2"
+ # File.open('t.csv', 'w') do |out_io|
+ # CSV.filter(in_string, out_io) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
+ # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # \IO stream output with headers:
+ #
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
+ # File.open('t.csv', 'w') do |out_io|
+ # CSV.filter(in_string, out_io, headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
+ # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
+ #
+ # When neither +in_string_or_io+ nor +out_string_or_io+ given,
+ # parses from {ARGF}[rdoc-ref:ARGF]
+ # and generates to STDOUT.
+ #
+ # Without headers:
+ #
+ # # Put Ruby code into a file.
+ # ruby = <<-EOT
+ # require 'csv'
+ # CSV.filter do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # EOT
+ # File.write('t.rb', ruby)
+ # # Put some CSV into a file.
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
+ # # Run the Ruby code with CSV filename as argument.
+ # system(Gem.ruby, "t.rb", "t.csv")
+ #
+ # Output (to STDOUT):
+ #
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # With headers:
+ #
+ # # Put Ruby code into a file.
+ # ruby = <<-EOT
+ # require 'csv'
+ # CSV.filter(headers: true) do |row|
+ # row[0].upcase!
+ # row[1] = - row[1].to_i
+ # end
+ # EOT
+ # File.write('t.rb', ruby)
+ # # Put some CSV into a file.
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
+ # # Run the Ruby code with CSV filename as argument.
+ # system(Gem.ruby, "t.rb", "t.csv")
+ #
+ # Output (to STDOUT):
+ #
+ # Name,Value
+ # FOO,0
+ # BAR,-1
+ # BAZ,-2
+ #
+ # Arguments:
+ #
+ # * Argument +in_string_or_io+ must be a \String or an \IO stream.
+ # * Argument +out_string_or_io+ must be a \String or an \IO stream.
+ # * Arguments <tt>**options</tt> must be keyword options.
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
def filter(input=nil, output=nil, **options)
# parse options for input, output, or both
in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
options.each do |key, value|
- case key.to_s
+ case key
when /\Ain(?:put)?_(.+)\Z/
in_options[$1.to_sym] = value
when /\Aout(?:put)?_(.+)\Z/
@@ -1107,111 +1244,90 @@ class CSV
#
# :call-seq:
- # foreach(path, mode='r', **options) {|row| ... )
- # foreach(io, mode='r', **options {|row| ... )
- # foreach(path, mode='r', headers: ..., **options) {|row| ... )
- # foreach(io, mode='r', headers: ..., **options {|row| ... )
- # foreach(path, mode='r', **options) -> new_enumerator
- # foreach(io, mode='r', **options -> new_enumerator
+ # foreach(path_or_io, mode='r', **options) {|row| ... )
+ # foreach(path_or_io, mode='r', **options) -> new_enumerator
#
- # Calls the block with each row read from source +path+ or +io+.
+ # Calls the block with each row read from source +path_or_io+.
#
- # * Argument +path+, if given, must be the path to a file.
- # :include: ../doc/csv/arguments/io.rdoc
- # * Argument +mode+, if given, must be a \File mode
- # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
- # * Arguments <tt>**options</tt> must be keyword options.
- # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
- # * This method optionally accepts an additional <tt>:encoding</tt> option
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
- # You must provide this unless your data is in the encoding
- # given by <tt>Encoding::default_external</tt>.
- # Parsing will use this to determine how to parse the data.
- # You may provide a second Encoding to
- # have the data transcoded as it is read. For example,
- # encoding: 'UTF-32BE:UTF-8'
- # would read +UTF-32BE+ data from the file
- # but transcode it to +UTF-8+ before parsing.
- #
- # ====== Without Option +headers+
+ # \Path input without headers:
#
- # Without option +headers+, returns each row as an \Array object.
- #
- # These examples assume prior execution of:
# string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
+ # in_path = 't.csv'
+ # File.write(in_path, string)
+ # CSV.foreach(in_path) {|row| p row }
#
- # Read rows from a file at +path+:
- # CSV.foreach(path) {|row| p row }
# Output:
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
#
- # Read rows from an \IO object:
- # File.open(path) do |file|
- # CSV.foreach(file) {|row| p row }
- # end
- #
- # Output:
# ["foo", "0"]
# ["bar", "1"]
# ["baz", "2"]
#
- # Returns a new \Enumerator if no block given:
- # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
- # CSV.foreach(File.open(path)) # => #<Enumerator: CSV:foreach(#<File:t.csv>, "r")>
+ # \Path input with headers:
+ #
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # in_path = 't.csv'
+ # File.write(in_path, string)
+ # CSV.foreach(in_path, headers: true) {|row| p row }
#
- # Issues a warning if an encoding is unsupported:
- # CSV.foreach(File.open(path), encoding: 'foo:bar') {|row| }
# Output:
- # warning: Unsupported encoding foo ignored
- # warning: Unsupported encoding bar ignored
#
- # ====== With Option +headers+
+ # <CSV::Row "Name":"foo" "Value":"0">
+ # <CSV::Row "Name":"bar" "Value":"1">
+ # <CSV::Row "Name":"baz" "Value":"2">
#
- # With {option +headers+}[#class-CSV-label-Option+headers],
- # returns each row as a CSV::Row object.
+ # \IO stream input without headers:
#
- # These examples assume prior execution of:
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
+ # string = "foo,0\nbar,1\nbaz,2\n"
# path = 't.csv'
# File.write(path, string)
- #
- # Read rows from a file at +path+:
- # CSV.foreach(path, headers: true) {|row| p row }
+ # File.open('t.csv') do |in_io|
+ # CSV.foreach(in_io) {|row| p row }
+ # end
#
# Output:
- # #<CSV::Row "Name":"foo" "Count":"0">
- # #<CSV::Row "Name":"bar" "Count":"1">
- # #<CSV::Row "Name":"baz" "Count":"2">
#
- # Read rows from an \IO object:
- # File.open(path) do |file|
- # CSV.foreach(file, headers: true) {|row| p row }
+ # ["foo", "0"]
+ # ["bar", "1"]
+ # ["baz", "2"]
+ #
+ # \IO stream input with headers:
+ #
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # path = 't.csv'
+ # File.write(path, string)
+ # File.open('t.csv') do |in_io|
+ # CSV.foreach(in_io, headers: true) {|row| p row }
# end
#
# Output:
- # #<CSV::Row "Name":"foo" "Count":"0">
- # #<CSV::Row "Name":"bar" "Count":"1">
- # #<CSV::Row "Name":"baz" "Count":"2">
- #
- # ---
#
- # Raises an exception if +path+ is a \String, but not the path to a readable file:
- # # Raises Errno::ENOENT (No such file or directory @ rb_sysopen - nosuch.csv):
- # CSV.foreach('nosuch.csv') {|row| }
+ # <CSV::Row "Name":"foo" "Value":"0">
+ # <CSV::Row "Name":"bar" "Value":"1">
+ # <CSV::Row "Name":"baz" "Value":"2">
#
- # Raises an exception if +io+ is an \IO object, but not open for reading:
- # io = File.open(path, 'w') {|row| }
- # # Raises TypeError (no implicit conversion of nil into String):
- # CSV.foreach(io) {|row| }
+ # With no block given, returns an \Enumerator:
#
- # Raises an exception if +mode+ is invalid:
- # # Raises ArgumentError (invalid access mode nosuch):
- # CSV.foreach(path, 'nosuch') {|row| }
+ # string = "foo,0\nbar,1\nbaz,2\n"
+ # path = 't.csv'
+ # File.write(path, string)
+ # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
#
+ # Arguments:
+ # * Argument +path_or_io+ must be a file path or an \IO stream.
+ # * Argument +mode+, if given, must be a \File mode
+ # See {Open Mode}[https://ruby-doc.org/core/IO.html#method-c-new-label-Open+Mode].
+ # * Arguments <tt>**options</tt> must be keyword options.
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
+ # that you can use to specify the Encoding of the data read from +path+ or +io+.
+ # You must provide this unless your data is in the encoding
+ # given by <tt>Encoding::default_external</tt>.
+ # Parsing will use this to determine how to parse the data.
+ # You may provide a second Encoding to
+ # have the data transcoded as it is read. For example,
+ # encoding: 'UTF-32BE:UTF-8'
+ # would read +UTF-32BE+ data from the file
+ # but transcode it to +UTF-8+ before parsing.
def foreach(path, mode="r", **options, &block)
return to_enum(__method__, path, mode, **options) unless block_given?
open(path, mode, **options) do |csv|
@@ -1349,6 +1465,46 @@ class CSV
(new(str, **options) << row).string
end
+ # :call-seq:
+ # CSV.generate_lines(rows)
+ # CSV.generate_lines(rows, **options)
+ #
+ # Returns the \String created by generating \CSV from
+ # using the specified +options+.
+ #
+ # Argument +rows+ must be an \Array of row. Row is \Array of \String or \CSV::Row.
+ #
+ # Special options:
+ # * Option <tt>:row_sep</tt> defaults to <tt>"\n"</tt> on Ruby 3.0 or later
+ # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
+ # $INPUT_RECORD_SEPARATOR # => "\n"
+ # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
+ # Encoding for the output. This method will try to guess your Encoding from
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
+ # this parameter as a backup plan.
+ #
+ # For other +options+,
+ # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
+ #
+ # ---
+ #
+ # Returns the \String generated from an
+ # CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"
+ #
+ # ---
+ #
+ # Raises an exception
+ # # Raises NoMethodError (undefined method `each' for :foo:Symbol)
+ # CSV.generate_lines(:foo)
+ #
+ def generate_lines(rows, **options)
+ self.generate(**options) do |csv|
+ rows.each do |row|
+ csv << row
+ end
+ end
+ end
+
#
# :call-seq:
# open(file_path, mode = "rb", **options ) -> new_csv
@@ -1357,7 +1513,7 @@ class CSV
# open(io, mode = "rb", **options ) { |csv| ... } -> object
#
# possible options elements:
- # hash form:
+ # keyword form:
# :invalid => nil # raise error on invalid byte sequence (default)
# :invalid => :replace # replace invalid byte sequence
# :undef => :replace # replace undefined conversion
@@ -1424,10 +1580,14 @@ class CSV
def open(filename, mode="r", **options)
# wrap a File opened with the remaining +args+ with no newline
# decorator
- file_opts = {universal_newline: false}.merge(options)
+ file_opts = options.dup
+ unless file_opts.key?(:newline)
+ file_opts[:universal_newline] ||= false
+ end
options.delete(:invalid)
options.delete(:undef)
options.delete(:replace)
+ options.delete_if {|k, _| /newline\z/.match?(k)}
begin
f = File.open(filename, mode, **file_opts)
@@ -1746,6 +1906,7 @@ class CSV
row_sep: :auto,
quote_char: '"',
field_size_limit: nil,
+ max_field_size: nil,
converters: nil,
unconverted_fields: nil,
headers: false,
@@ -1769,8 +1930,19 @@ class CSV
raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
if data.is_a?(String)
+ if encoding
+ if encoding.is_a?(String)
+ data_external_encoding, data_internal_encoding = encoding.split(":", 2)
+ if data_internal_encoding
+ data = data.encode(data_internal_encoding, data_external_encoding)
+ else
+ data = data.dup.force_encoding(data_external_encoding)
+ end
+ else
+ data = data.dup.force_encoding(encoding)
+ end
+ end
@io = StringIO.new(data)
- @io.set_encoding(encoding || data.encoding)
else
@io = data
end
@@ -1788,11 +1960,14 @@ class CSV
@initial_header_converters = header_converters
@initial_write_converters = write_converters
+ if max_field_size.nil? and field_size_limit
+ max_field_size = field_size_limit - 1
+ end
@parser_options = {
column_separator: col_sep,
row_separator: row_sep,
quote_character: quote_char,
- field_size_limit: field_size_limit,
+ max_field_size: max_field_size,
unconverted_fields: unconverted_fields,
headers: headers,
return_headers: return_headers,
@@ -1860,11 +2035,25 @@ class CSV
# Returns the limit for field size; used for parsing;
# see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
# CSV.new('').field_size_limit # => nil
+ #
+ # Deprecated since 3.2.3. Use +max_field_size+ instead.
def field_size_limit
parser.field_size_limit
end
# :call-seq:
+ # csv.max_field_size -> integer or nil
+ #
+ # Returns the limit for field size; used for parsing;
+ # see {Option +max_field_size+}[#class-CSV-label-Option+max_field_size]:
+ # CSV.new('').max_field_size # => nil
+ #
+ # Since 3.2.3.
+ def max_field_size
+ parser.max_field_size
+ end
+
+ # :call-seq:
# csv.skip_lines -> regexp or nil
#
# Returns the \Regexp used to identify comment lines; used for parsing;
@@ -1994,7 +2183,7 @@ class CSV
end
# :call-seq:
- # csv.encoding -> endcoding
+ # csv.encoding -> encoding
#
# Returns the encoding used for parsing and generating;
# see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
@@ -2362,7 +2551,13 @@ class CSV
# p row
# end
def each(&block)
- parser_enumerator.each(&block)
+ return to_enum(__method__) unless block_given?
+ begin
+ while true
+ yield(parser_enumerator.next)
+ end
+ rescue StopIteration
+ end
end
# :call-seq:
diff --git a/lib/csv/fields_converter.rb b/lib/csv/fields_converter.rb
index b206118d99..d15977d379 100644
--- a/lib/csv/fields_converter.rb
+++ b/lib/csv/fields_converter.rb
@@ -44,7 +44,7 @@ class CSV
@converters.empty?
end
- def convert(fields, headers, lineno)
+ def convert(fields, headers, lineno, quoted_fields)
return fields unless need_convert?
fields.collect.with_index do |field, index|
@@ -63,7 +63,8 @@ class CSV
else
header = nil
end
- field = converter[field, FieldInfo.new(index, lineno, header)]
+ quoted = quoted_fields[index]
+ field = converter[field, FieldInfo.new(index, lineno, header, quoted)]
end
break unless field.is_a?(String) # short-circuit pipeline for speed
end
diff --git a/lib/csv/input_record_separator.rb b/lib/csv/input_record_separator.rb
index bbf13479f7..7a99343c0c 100644
--- a/lib/csv/input_record_separator.rb
+++ b/lib/csv/input_record_separator.rb
@@ -4,20 +4,7 @@ require "stringio"
class CSV
module InputRecordSeparator
class << self
- is_input_record_separator_deprecated = false
- verbose, $VERBOSE = $VERBOSE, true
- stderr, $stderr = $stderr, StringIO.new
- input_record_separator = $INPUT_RECORD_SEPARATOR
- begin
- $INPUT_RECORD_SEPARATOR = "\r\n"
- is_input_record_separator_deprecated = (not $stderr.string.empty?)
- ensure
- $INPUT_RECORD_SEPARATOR = input_record_separator
- $stderr = stderr
- $VERBOSE = verbose
- end
-
- if is_input_record_separator_deprecated
+ if RUBY_VERSION >= "3.0.0"
def value
"\n"
end
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
index 7e943acf21..afb3131cd5 100644
--- a/lib/csv/parser.rb
+++ b/lib/csv/parser.rb
@@ -2,15 +2,10 @@
require "strscan"
-require_relative "delete_suffix"
require_relative "input_record_separator"
-require_relative "match_p"
require_relative "row"
require_relative "table"
-using CSV::DeleteSuffix if CSV.const_defined?(:DeleteSuffix)
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
class CSV
# Note: Don't use this class directly. This is an internal class.
class Parser
@@ -27,6 +22,10 @@ class CSV
class InvalidEncoding < StandardError
end
+ # Raised when unexpected case is happen.
+ class UnexpectedError < StandardError
+ end
+
#
# CSV::Scanner receives a CSV output, scans it and return the content.
# It also controls the life cycle of the object with its methods +keep_start+,
@@ -78,10 +77,10 @@ class CSV
# +keep_end+, +keep_back+, +keep_drop+.
#
# CSV::InputsScanner.scan() tries to match with pattern at the current position.
- # If there's a match, the scanner advances the “scan pointer” and returns the matched string.
+ # If there's a match, the scanner advances the "scan pointer" and returns the matched string.
# Otherwise, the scanner returns nil.
#
- # CSV::InputsScanner.rest() returns the “rest” of the string (i.e. everything after the scan pointer).
+ # CSV::InputsScanner.rest() returns the "rest" of the string (i.e. everything after the scan pointer).
# If there is no more data (eos? = true), it returns "".
#
class InputsScanner
@@ -96,11 +95,13 @@ class CSV
end
def each_line(row_separator)
+ return enum_for(__method__, row_separator) unless block_given?
buffer = nil
input = @scanner.rest
position = @scanner.pos
offset = 0
n_row_separator_chars = row_separator.size
+ # trace(__method__, :start, line, input)
while true
input.each_line(row_separator) do |line|
@scanner.pos += line.bytesize
@@ -140,25 +141,28 @@ class CSV
end
def scan(pattern)
+ # trace(__method__, pattern, :start)
value = @scanner.scan(pattern)
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
return value if @last_scanner
- if value
- read_chunk if @scanner.eos?
- return value
- else
- nil
- end
+ read_chunk if value and @scanner.eos?
+ # trace(__method__, pattern, :done, value)
+ value
end
def scan_all(pattern)
+ # trace(__method__, pattern, :start)
value = @scanner.scan(pattern)
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
return value if @last_scanner
return nil if value.nil?
while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
+ # trace(__method__, pattern, :sub, sub_value)
value << sub_value
end
+ # trace(__method__, pattern, :done, value)
value
end
@@ -167,68 +171,126 @@ class CSV
end
def keep_start
- @keeps.push([@scanner.pos, nil])
+ # trace(__method__, :start)
+ adjust_last_keep
+ @keeps.push([@scanner, @scanner.pos, nil])
+ # trace(__method__, :done)
end
def keep_end
- start, buffer = @keeps.pop
- keep = @scanner.string.byteslice(start, @scanner.pos - start)
+ # trace(__method__, :start)
+ scanner, start, buffer = @keeps.pop
+ if scanner == @scanner
+ keep = @scanner.string.byteslice(start, @scanner.pos - start)
+ else
+ keep = @scanner.string.byteslice(0, @scanner.pos)
+ end
if buffer
buffer << keep
keep = buffer
end
+ # trace(__method__, :done, keep)
keep
end
def keep_back
- start, buffer = @keeps.pop
+ # trace(__method__, :start)
+ scanner, start, buffer = @keeps.pop
if buffer
+ # trace(__method__, :rescan, start, buffer)
string = @scanner.string
- keep = string.byteslice(start, string.bytesize - start)
+ if scanner == @scanner
+ keep = string.byteslice(start, string.bytesize - start)
+ else
+ keep = string
+ end
if keep and not keep.empty?
@inputs.unshift(StringIO.new(keep))
@last_scanner = false
end
@scanner = StringScanner.new(buffer)
else
+ if @scanner != scanner
+ message = "scanners are different but no buffer: "
+ message += "#{@scanner.inspect}(#{@scanner.object_id}): "
+ message += "#{scanner.inspect}(#{scanner.object_id})"
+ raise UnexpectedError, message
+ end
+ # trace(__method__, :repos, start, buffer)
@scanner.pos = start
end
read_chunk if @scanner.eos?
end
def keep_drop
- @keeps.pop
+ _, _, buffer = @keeps.pop
+ # trace(__method__, :done, :empty) unless buffer
+ return unless buffer
+
+ last_keep = @keeps.last
+ # trace(__method__, :done, :no_last_keep) unless last_keep
+ return unless last_keep
+
+ if last_keep[2]
+ last_keep[2] << buffer
+ else
+ last_keep[2] = buffer
+ end
+ # trace(__method__, :done)
end
def rest
@scanner.rest
end
+ def check(pattern)
+ @scanner.check(pattern)
+ end
+
private
- def read_chunk
- return false if @last_scanner
+ def trace(*args)
+ pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
+ end
- unless @keeps.empty?
- keep = @keeps.last
- keep_start = keep[0]
- string = @scanner.string
- keep_data = string.byteslice(keep_start, @scanner.pos - keep_start)
- if keep_data
- keep_buffer = keep[1]
- if keep_buffer
- keep_buffer << keep_data
- else
- keep[1] = keep_data.dup
- end
+ def adjust_last_keep
+ # trace(__method__, :start)
+
+ keep = @keeps.last
+ # trace(__method__, :done, :empty) if keep.nil?
+ return if keep.nil?
+
+ scanner, start, buffer = keep
+ string = @scanner.string
+ if @scanner != scanner
+ start = 0
+ end
+ if start == 0 and @scanner.eos?
+ keep_data = string
+ else
+ keep_data = string.byteslice(start, @scanner.pos - start)
+ end
+ if keep_data
+ if buffer
+ buffer << keep_data
+ else
+ keep[2] = keep_data.dup
end
- keep[0] = 0
end
+ # trace(__method__, :done)
+ end
+
+ def read_chunk
+ return false if @last_scanner
+
+ adjust_last_keep
+
input = @inputs.first
case input
when StringIO
string = input.read
raise InvalidEncoding unless string.valid_encoding?
+ # trace(__method__, :stringio, string)
@scanner = StringScanner.new(string)
@inputs.shift
@last_scanner = @inputs.empty?
@@ -237,6 +299,7 @@ class CSV
chunk = input.gets(@row_separator, @chunk_size)
if chunk
raise InvalidEncoding unless chunk.valid_encoding?
+ # trace(__method__, :chunk, chunk)
@scanner = StringScanner.new(chunk)
if input.respond_to?(:eof?) and input.eof?
@inputs.shift
@@ -244,6 +307,7 @@ class CSV
end
true
else
+ # trace(__method__, :no_chunk)
@scanner = StringScanner.new("".encode(@encoding))
@inputs.shift
@last_scanner = @inputs.empty?
@@ -278,7 +342,11 @@ class CSV
end
def field_size_limit
- @field_size_limit
+ @max_field_size&.succ
+ end
+
+ def max_field_size
+ @max_field_size
end
def skip_lines
@@ -346,6 +414,16 @@ class CSV
end
message = "Invalid byte sequence in #{@encoding}"
raise MalformedCSVError.new(message, lineno)
+ rescue UnexpectedError => error
+ if @scanner
+ ignore_broken_line
+ lineno = @lineno
+ else
+ lineno = @lineno + 1
+ end
+ message = "This should not be happen: #{error.message}: "
+ message += "Please report this to https://github.com/ruby/csv/issues"
+ raise MalformedCSVError.new(message, lineno)
end
end
@@ -390,7 +468,7 @@ class CSV
@backslash_quote = false
end
@unconverted_fields = @options[:unconverted_fields]
- @field_size_limit = @options[:field_size_limit]
+ @max_field_size = @options[:max_field_size]
@skip_blanks = @options[:skip_blanks]
@fields_converter = @options[:fields_converter]
@header_fields_converter = @options[:header_fields_converter]
@@ -680,9 +758,10 @@ class CSV
case headers
when Array
@raw_headers = headers
+ quoted_fields = [false] * @raw_headers.size
@use_headers = true
when String
- @raw_headers = parse_headers(headers)
+ @raw_headers, quoted_fields = parse_headers(headers)
@use_headers = true
when nil, false
@raw_headers = nil
@@ -692,21 +771,28 @@ class CSV
@use_headers = true
end
if @raw_headers
- @headers = adjust_headers(@raw_headers)
+ @headers = adjust_headers(@raw_headers, quoted_fields)
else
@headers = nil
end
end
def parse_headers(row)
- CSV.parse_line(row,
- col_sep: @column_separator,
- row_sep: @row_separator,
- quote_char: @quote_character)
+ quoted_fields = []
+ converter = lambda do |field, info|
+ quoted_fields << info.quoted?
+ field
+ end
+ headers = CSV.parse_line(row,
+ col_sep: @column_separator,
+ row_sep: @row_separator,
+ quote_char: @quote_character,
+ converters: [converter])
+ [headers, quoted_fields]
end
- def adjust_headers(headers)
- adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno)
+ def adjust_headers(headers, quoted_fields)
+ adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno, quoted_fields)
adjusted_headers.each {|h| h.freeze if h.is_a? String}
adjusted_headers
end
@@ -729,28 +815,28 @@ class CSV
sample[0, 128].index(@quote_character)
end
- SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
- if SCANNER_TEST
- class UnoptimizedStringIO
- def initialize(string)
- @io = StringIO.new(string, "rb:#{string.encoding}")
- end
+ class UnoptimizedStringIO # :nodoc:
+ def initialize(string)
+ @io = StringIO.new(string, "rb:#{string.encoding}")
+ end
- def gets(*args)
- @io.gets(*args)
- end
+ def gets(*args)
+ @io.gets(*args)
+ end
- def each_line(*args, &block)
- @io.each_line(*args, &block)
- end
+ def each_line(*args, &block)
+ @io.each_line(*args, &block)
+ end
- def eof?
- @io.eof?
- end
+ def eof?
+ @io.eof?
end
+ end
- SCANNER_TEST_CHUNK_SIZE =
- Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
+ SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
+ if SCANNER_TEST
+ SCANNER_TEST_CHUNK_SIZE_NAME = "CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"
+ SCANNER_TEST_CHUNK_SIZE_VALUE = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
def build_scanner
inputs = @samples.collect do |sample|
UnoptimizedStringIO.new(sample)
@@ -760,10 +846,17 @@ class CSV
else
inputs << @input
end
+ begin
+ chunk_size_value = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
+ rescue # Ractor::IsolationError
+ # Ractor on Ruby 3.0 can't read ENV value.
+ chunk_size_value = SCANNER_TEST_CHUNK_SIZE_VALUE
+ end
+ chunk_size = Integer((chunk_size_value || "1"), 10)
InputsScanner.new(inputs,
@encoding,
@row_separator,
- chunk_size: SCANNER_TEST_CHUNK_SIZE)
+ chunk_size: chunk_size)
end
else
def build_scanner
@@ -826,6 +919,14 @@ class CSV
end
end
+ def validate_field_size(field)
+ return unless @max_field_size
+ return if field.size <= @max_field_size
+ ignore_broken_line
+ message = "Field size exceeded: #{field.size} > #{@max_field_size}"
+ raise MalformedCSVError.new(message, @lineno)
+ end
+
def parse_no_quote(&block)
@scanner.each_line(@row_separator) do |line|
next if @skip_lines and skip_line?(line)
@@ -835,9 +936,16 @@ class CSV
if line.empty?
next if @skip_blanks
row = []
+ quoted_fields = []
else
line = strip_value(line)
row = line.split(@split_column_separator, -1)
+ quoted_fields = [false] * row.size
+ if @max_field_size
+ row.each do |column|
+ validate_field_size(column)
+ end
+ end
n_columns = row.size
i = 0
while i < n_columns
@@ -846,7 +954,7 @@ class CSV
end
end
@last_line = original_line
- emit_row(row, &block)
+ emit_row(row, quoted_fields, &block)
end
end
@@ -868,31 +976,37 @@ class CSV
next
end
row = []
+ quoted_fields = []
elsif line.include?(@cr) or line.include?(@lf)
@scanner.keep_back
@need_robust_parsing = true
return parse_quotable_robust(&block)
else
row = line.split(@split_column_separator, -1)
+ quoted_fields = []
n_columns = row.size
i = 0
while i < n_columns
column = row[i]
if column.empty?
+ quoted_fields << false
row[i] = nil
else
n_quotes = column.count(@quote_character)
if n_quotes.zero?
+ quoted_fields << false
# no quote
elsif n_quotes == 2 and
column.start_with?(@quote_character) and
column.end_with?(@quote_character)
+ quoted_fields << true
row[i] = column[1..-2]
else
@scanner.keep_back
@need_robust_parsing = true
return parse_quotable_robust(&block)
end
+ validate_field_size(row[i])
end
i += 1
end
@@ -900,13 +1014,14 @@ class CSV
@scanner.keep_drop
@scanner.keep_start
@last_line = original_line
- emit_row(row, &block)
+ emit_row(row, quoted_fields, &block)
end
@scanner.keep_drop
end
def parse_quotable_robust(&block)
row = []
+ quoted_fields = []
skip_needless_lines
start_row
while true
@@ -916,32 +1031,39 @@ class CSV
value = parse_column_value
if value
@scanner.scan_all(@strip_value) if @strip_value
- if @field_size_limit and value.size >= @field_size_limit
- ignore_broken_line
- raise MalformedCSVError.new("Field size exceeded", @lineno)
- end
+ validate_field_size(value)
end
if parse_column_end
row << value
+ quoted_fields << @quoted_column_value
elsif parse_row_end
if row.empty? and value.nil?
- emit_row([], &block) unless @skip_blanks
+ emit_row([], [], &block) unless @skip_blanks
else
row << value
- emit_row(row, &block)
+ quoted_fields << @quoted_column_value
+ emit_row(row, quoted_fields, &block)
row = []
+ quoted_fields = []
end
skip_needless_lines
start_row
elsif @scanner.eos?
break if row.empty? and value.nil?
row << value
- emit_row(row, &block)
+ quoted_fields << @quoted_column_value
+ emit_row(row, quoted_fields, &block)
break
else
if @quoted_column_value
+ if liberal_parsing? and (new_line = @scanner.check(@line_end))
+ message =
+ "Illegal end-of-line sequence outside of a quoted field " +
+ "<#{new_line.inspect}>"
+ else
+ message = "Any value after quoted field isn't allowed"
+ end
ignore_broken_line
- message = "Any value after quoted field isn't allowed"
raise MalformedCSVError.new(message, @lineno)
elsif @unquoted_column_value and
(new_line = @scanner.scan(@line_end))
@@ -1034,7 +1156,7 @@ class CSV
if (n_quotes % 2).zero?
quotes[0, (n_quotes - 2) / 2]
else
- value = quotes[0, (n_quotes - 1) / 2]
+ value = quotes[0, n_quotes / 2]
while true
quoted_value = @scanner.scan_all(@quoted_value)
value << quoted_value if quoted_value
@@ -1058,11 +1180,9 @@ class CSV
n_quotes = quotes.size
if n_quotes == 1
break
- elsif (n_quotes % 2) == 1
- value << quotes[0, (n_quotes - 1) / 2]
- break
else
value << quotes[0, n_quotes / 2]
+ break if (n_quotes % 2) == 1
end
end
value
@@ -1098,18 +1218,15 @@ class CSV
def strip_value(value)
return value unless @strip
- return nil if value.nil?
+ return value if value.nil?
case @strip
when String
- size = value.size
- while value.start_with?(@strip)
- size -= 1
- value = value[1, size]
+ while value.delete_prefix!(@strip)
+ # do nothing
end
- while value.end_with?(@strip)
- size -= 1
- value = value[0, size]
+ while value.delete_suffix!(@strip)
+ # do nothing
end
else
value.strip!
@@ -1132,22 +1249,22 @@ class CSV
@scanner.keep_start
end
- def emit_row(row, &block)
+ def emit_row(row, quoted_fields, &block)
@lineno += 1
raw_row = row
if @use_headers
if @headers.nil?
- @headers = adjust_headers(row)
+ @headers = adjust_headers(row, quoted_fields)
return unless @return_headers
row = Row.new(@headers, row, true)
else
row = Row.new(@headers,
- @fields_converter.convert(raw_row, @headers, @lineno))
+ @fields_converter.convert(raw_row, @headers, @lineno, quoted_fields))
end
else
# convert fields, if needed...
- row = @fields_converter.convert(raw_row, nil, @lineno)
+ row = @fields_converter.convert(raw_row, nil, @lineno, quoted_fields)
end
# inject unconverted fields and accessor, if requested...
diff --git a/lib/csv/row.rb b/lib/csv/row.rb
index 0f465ea2a3..86323f7d0a 100644
--- a/lib/csv/row.rb
+++ b/lib/csv/row.rb
@@ -3,30 +3,105 @@
require "forwardable"
class CSV
+ # = \CSV::Row
+ # A \CSV::Row instance represents a \CSV table row.
+ # (see {class CSV}[../CSV.html]).
#
- # A CSV::Row is part Array and part Hash. It retains an order for the fields
- # and allows duplicates just as an Array would, but also allows you to access
- # fields by name just as you could if they were in a Hash.
+ # The instance may have:
+ # - Fields: each is an object, not necessarily a \String.
+ # - Headers: each serves a key, and also need not be a \String.
#
- # All rows returned by CSV will be constructed from this class, if header row
- # processing is activated.
+ # === Instance Methods
+ #
+ # \CSV::Row has three groups of instance methods:
+ # - Its own internally defined instance methods.
+ # - Methods included by module Enumerable.
+ # - Methods delegated to class Array.:
+ # * Array#empty?
+ # * Array#length
+ # * Array#size
+ #
+ # == Creating a \CSV::Row Instance
+ #
+ # Commonly, a new \CSV::Row instance is created by parsing \CSV source
+ # that has headers:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.each {|row| p row }
+ # Output:
+ # #<CSV::Row "Name":"foo" "Value":"0">
+ # #<CSV::Row "Name":"bar" "Value":"1">
+ # #<CSV::Row "Name":"baz" "Value":"2">
+ #
+ # You can also create a row directly. See ::new.
+ #
+ # == Headers
+ #
+ # Like a \CSV::Table, a \CSV::Row has headers.
+ #
+ # A \CSV::Row that was created by parsing \CSV source
+ # inherits its headers from the table:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table.first
+ # row.headers # => ["Name", "Value"]
+ #
+ # You can also create a new row with headers;
+ # like the keys in a \Hash, the headers need not be Strings:
+ # row = CSV::Row.new([:name, :value], ['foo', 0])
+ # row.headers # => [:name, :value]
+ #
+ # The new row retains its headers even if added to a table
+ # that has headers:
+ # table << row # => #<CSV::Table mode:col_or_row row_count:5>
+ # row.headers # => [:name, :value]
+ # row[:name] # => "foo"
+ # row['Name'] # => nil
+ #
+ #
+ #
+ # == Accessing Fields
+ #
+ # You may access a field in a \CSV::Row with either its \Integer index
+ # (\Array-style) or its header (\Hash-style).
+ #
+ # Fetch a field using method #[]:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row[1] # => 0
+ # row['Value'] # => 0
+ #
+ # Set a field using method #[]=:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ # row[0] = 'bar'
+ # row['Value'] = 1
+ # row # => #<CSV::Row "Name":"bar" "Value":1>
#
class Row
- #
- # Constructs a new CSV::Row from +headers+ and +fields+, which are expected
- # to be Arrays. If one Array is shorter than the other, it will be padded
- # with +nil+ objects.
- #
- # The optional +header_row+ parameter can be set to +true+ to indicate, via
- # CSV::Row.header_row?() and CSV::Row.field_row?(), that this is a header
- # row. Otherwise, the row assumes to be a field row.
- #
- # A CSV::Row object supports the following Array methods through delegation:
- #
- # * empty?()
- # * length()
- # * size()
- #
+ # :call-seq:
+ # CSV::Row.new(headers, fields, header_row = false) -> csv_row
+ #
+ # Returns the new \CSV::Row instance constructed from
+ # arguments +headers+ and +fields+; both should be Arrays;
+ # note that the fields need not be Strings:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ #
+ # If the \Array lengths are different, the shorter is +nil+-filled:
+ # row = CSV::Row.new(['Name', 'Value', 'Date', 'Size'], ['foo', 0])
+ # row # => #<CSV::Row "Name":"foo" "Value":0 "Date":nil "Size":nil>
+ #
+ # Each \CSV::Row object is either a <i>field row</i> or a <i>header row</i>;
+ # by default, a new row is a field row; for the row created above:
+ # row.field_row? # => true
+ # row.header_row? # => false
+ #
+ # If the optional argument +header_row+ is given as +true+,
+ # the created row is a header row:
+ # row = CSV::Row.new(['Name', 'Value'], ['foo', 0], header_row = true)
+ # row # => #<CSV::Row "Name":"foo" "Value":0>
+ # row.field_row? # => false
+ # row.header_row? # => true
def initialize(headers, fields, header_row = false)
@header_row = header_row
headers.each { |h| h.freeze if h.is_a? String }
@@ -48,6 +123,10 @@ class CSV
extend Forwardable
def_delegators :@row, :empty?, :length, :size
+ # :call-seq:
+ # row.initialize_copy(other_row) -> self
+ #
+ # Calls superclass method.
def initialize_copy(other)
super_return_value = super
@row = @row.collect(&:dup)
@@ -71,7 +150,7 @@ class CSV
end
# :call-seq:
- # row.headers
+ # row.headers -> array_of_headers
#
# Returns the headers for this row:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
@@ -83,9 +162,9 @@ class CSV
end
# :call-seq:
- # field(index)
- # field(header)
- # field(header, offset)
+ # field(index) -> value
+ # field(header) -> value
+ # field(header, offset) -> value
#
# Returns the field value for the given +index+ or +header+.
#
@@ -137,9 +216,9 @@ class CSV
#
# :call-seq:
- # fetch(header)
- # fetch(header, default)
- # fetch(header) {|row| ... }
+ # fetch(header) -> value
+ # fetch(header, default) -> value
+ # fetch(header) {|row| ... } -> value
#
# Returns the field value as specified by +header+.
#
@@ -193,7 +272,7 @@ class CSV
end
# :call-seq:
- # row.has_key?(header)
+ # row.has_key?(header) -> true or false
#
# Returns +true+ if there is a field with the given +header+,
# +false+ otherwise.
@@ -320,7 +399,7 @@ class CSV
end
# :call-seq:
- # row.push(*values) ->self
+ # row.push(*values) -> self
#
# Appends each of the given +values+ to +self+ as a field; returns +self+:
# source = "Name,Name,Name\nFoo,Bar,Baz\n"
@@ -403,7 +482,7 @@ class CSV
end
# :call-seq:
- # self.fields(*specifiers)
+ # self.fields(*specifiers) -> array_of_fields
#
# Returns field values per the given +specifiers+, which may be any mixture of:
# - \Integer index.
@@ -471,15 +550,26 @@ class CSV
end
alias_method :values_at, :fields
- #
# :call-seq:
- # index( header )
- # index( header, offset )
+ # index(header) -> index
+ # index(header, offset) -> index
#
- # This method will return the index of a field with the provided +header+.
- # The +offset+ can be used to locate duplicate header names, as described in
- # CSV::Row.field().
+ # Returns the index for the given header, if it exists;
+ # otherwise returns +nil+.
#
+ # With the single argument +header+, returns the index
+ # of the first-found field with the given +header+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.index('Name') # => 0
+ # row.index('NAME') # => nil
+ #
+ # With arguments +header+ and +offset+,
+ # returns the index of the first-found field with given +header+,
+ # but ignoring the first +offset+ fields:
+ # row.index('Name', 1) # => 1
+ # row.index('Name', 3) # => nil
def index(header, minimum_index = 0)
# find the pair
index = headers[minimum_index..-1].index(header)
@@ -487,24 +577,36 @@ class CSV
index.nil? ? nil : index + minimum_index
end
+ # :call-seq:
+ # row.field?(value) -> true or false
#
- # Returns +true+ if +data+ matches a field in this row, and +false+
- # otherwise.
- #
+ # Returns +true+ if +value+ is a field in this row, +false+ otherwise:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.field?('Bar') # => true
+ # row.field?('BAR') # => false
def field?(data)
fields.include? data
end
include Enumerable
+ # :call-seq:
+ # row.each {|header, value| ... } -> self
#
- # Yields each pair of the row as header and field tuples (much like
- # iterating over a Hash). This method returns the row for chaining.
- #
- # If no block is given, an Enumerator is returned.
- #
- # Support for Enumerable.
+ # Calls the block with each header-value pair; returns +self+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # row.each {|header, value| p [header, value] }
+ # Output:
+ # ["Name", "Foo"]
+ # ["Name", "Bar"]
+ # ["Name", "Baz"]
#
+ # If no block is given, returns a new Enumerator:
+ # row.each # => #<Enumerator: #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz">:each>
def each(&block)
return enum_for(__method__) { size } unless block_given?
@@ -515,10 +617,19 @@ class CSV
alias_method :each_pair, :each
+ # :call-seq:
+ # row == other -> true or false
#
- # Returns +true+ if this row contains the same headers and fields in the
- # same order as +other+.
- #
+ # Returns +true+ if +other+ is a /CSV::Row that has the same
+ # fields (headers and values) in the same order as +self+;
+ # otherwise returns +false+:
+ # source = "Name,Name,Name\nFoo,Bar,Baz\n"
+ # table = CSV.parse(source, headers: true)
+ # row = table[0]
+ # other_row = table[0]
+ # row == other_row # => true
+ # other_row = table[1]
+ # row == other_row # => false
def ==(other)
return @row == other.row if other.is_a? CSV::Row
@row == other
@@ -548,9 +659,31 @@ class CSV
end
alias_method :to_hash, :to_h
+ # :call-seq:
+ # row.deconstruct_keys(keys) -> hash
+ #
+ # Returns the new \Hash suitable for pattern matching containing only the
+ # keys specified as an argument.
+ def deconstruct_keys(keys)
+ if keys.nil?
+ to_h
+ else
+ keys.to_h { |key| [key, self[key]] }
+ end
+ end
+
alias_method :to_ary, :to_a
# :call-seq:
+ # row.deconstruct -> array
+ #
+ # Returns the new \Array suitable for pattern matching containing the values
+ # of the row.
+ def deconstruct
+ fields
+ end
+
+ # :call-seq:
# row.to_csv -> csv_string
#
# Returns the row as a \CSV String. Headers are not included:
@@ -570,7 +703,7 @@ class CSV
# by +index_or_header+ and +specifiers+.
#
# The nested objects may be instances of various classes.
- # See {Dig Methods}[https://docs.ruby-lang.org/en/master/doc/dig_methods_rdoc.html].
+ # See {Dig Methods}[rdoc-ref:dig_methods.rdoc].
#
# Examples:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
diff --git a/lib/csv/table.rb b/lib/csv/table.rb
index 1ce0dd6daf..fb19f5453f 100644
--- a/lib/csv/table.rb
+++ b/lib/csv/table.rb
@@ -3,31 +3,199 @@
require "forwardable"
class CSV
+ # = \CSV::Table
+ # A \CSV::Table instance represents \CSV data.
+ # (see {class CSV}[../CSV.html]).
#
- # A CSV::Table is a two-dimensional data structure for representing CSV
- # documents. Tables allow you to work with the data by row or column,
- # manipulate the data, and even convert the results back to CSV, if needed.
+ # The instance may have:
+ # - Rows: each is a Table::Row object.
+ # - Headers: names for the columns.
#
- # All tables returned by CSV will be constructed from this class, if header
- # row processing is activated.
+ # === Instance Methods
#
+ # \CSV::Table has three groups of instance methods:
+ # - Its own internally defined instance methods.
+ # - Methods included by module Enumerable.
+ # - Methods delegated to class Array.:
+ # * Array#empty?
+ # * Array#length
+ # * Array#size
+ #
+ # == Creating a \CSV::Table Instance
+ #
+ # Commonly, a new \CSV::Table instance is created by parsing \CSV source
+ # using headers:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.class # => CSV::Table
+ #
+ # You can also create an instance directly. See ::new.
+ #
+ # == Headers
+ #
+ # If a table has headers, the headers serve as labels for the columns of data.
+ # Each header serves as the label for its column.
+ #
+ # The headers for a \CSV::Table object are stored as an \Array of Strings.
+ #
+ # Commonly, headers are defined in the first row of \CSV source:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.headers # => ["Name", "Value"]
+ #
+ # If no headers are defined, the \Array is empty:
+ # table = CSV::Table.new([])
+ # table.headers # => []
+ #
+ # == Access Modes
+ #
+ # \CSV::Table provides three modes for accessing table data:
+ # - \Row mode.
+ # - Column mode.
+ # - Mixed mode (the default for a new table).
+ #
+ # The access mode for a\CSV::Table instance affects the behavior
+ # of some of its instance methods:
+ # - #[]
+ # - #[]=
+ # - #delete
+ # - #delete_if
+ # - #each
+ # - #values_at
+ #
+ # === \Row Mode
+ #
+ # Set a table to row mode with method #by_row!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ #
+ # Specify a single row by an \Integer index:
+ # # Get a row.
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
+ # # Set a row, then get it.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
+ #
+ # Specify a sequence of rows by a \Range:
+ # # Get rows.
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
+ # # Set rows, then get them.
+ # table[1..2] = [
+ # CSV::Row.new(['Name', 'Value'], ['bat', 4]),
+ # CSV::Row.new(['Name', 'Value'], ['bad', 5]),
+ # ]
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
+ #
+ # === Column Mode
+ #
+ # Set a table to column mode with method #by_col!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # Specify a column by an \Integer index:
+ # # Get a column.
+ # table[0]
+ # # Set a column, then get it.
+ # table[0] = ['FOO', 'BAR', 'BAZ']
+ # table[0] # => ["FOO", "BAR", "BAZ"]
+ #
+ # Specify a column by its \String header:
+ # # Get a column.
+ # table['Name'] # => ["FOO", "BAR", "BAZ"]
+ # # Set a column, then get it.
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
+ #
+ # === Mixed Mode
+ #
+ # In mixed mode, you can refer to either rows or columns:
+ # - An \Integer index refers to a row.
+ # - A \Range index refers to multiple rows.
+ # - A \String index refers to a column.
+ #
+ # Set a table to mixed mode with method #by_col_or_row!:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # Specify a single row by an \Integer index:
+ # # Get a row.
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
+ # # Set a row, then get it.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
+ #
+ # Specify a sequence of rows by a \Range:
+ # # Get rows.
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
+ # # Set rows, then get them.
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bat', 4])
+ # table[2] = CSV::Row.new(['Name', 'Value'], ['bad', 5])
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
+ #
+ # Specify a column by its \String header:
+ # # Get a column.
+ # table['Name'] # => ["foo", "bat", "bad"]
+ # # Set a column, then get it.
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
class Table
+ # :call-seq:
+ # CSV::Table.new(array_of_rows, headers = nil) -> csv_table
+ #
+ # Returns a new \CSV::Table object.
+ #
+ # - Argument +array_of_rows+ must be an \Array of CSV::Row objects.
+ # - Argument +headers+, if given, may be an \Array of Strings.
+ #
+ # ---
+ #
+ # Create an empty \CSV::Table object:
+ # table = CSV::Table.new([])
+ # table # => #<CSV::Table mode:col_or_row row_count:1>
+ #
+ # Create a non-empty \CSV::Table object:
+ # rows = [
+ # CSV::Row.new([], []),
+ # CSV::Row.new([], []),
+ # CSV::Row.new([], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
+ #
+ # ---
#
- # Constructs a new CSV::Table from +array_of_rows+, which are expected
- # to be CSV::Row objects. All rows are assumed to have the same headers.
+ # If argument +headers+ is an \Array of Strings,
+ # those Strings become the table's headers:
+ # table = CSV::Table.new([], headers: ['Name', 'Age'])
+ # table.headers # => ["Name", "Age"]
#
- # The optional +headers+ parameter can be set to Array of headers.
- # If headers aren't set, headers are fetched from CSV::Row objects.
- # Otherwise, headers() method will return headers being set in
- # headers argument.
+ # If argument +headers+ is not given and the table has rows,
+ # the headers are taken from the first row:
+ # rows = [
+ # CSV::Row.new(['Foo', 'Bar'], []),
+ # CSV::Row.new(['foo', 'bar'], []),
+ # CSV::Row.new(['FOO', 'BAR'], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table.headers # => ["Foo", "Bar"]
#
- # A CSV::Table object supports the following Array methods through
- # delegation:
+ # If argument +headers+ is not given and the table is empty (has no rows),
+ # the headers are also empty:
+ # table = CSV::Table.new([])
+ # table.headers # => []
#
- # * empty?()
- # * length()
- # * size()
+ # ---
#
+ # Raises an exception if argument +array_of_rows+ is not an \Array object:
+ # # Raises NoMethodError (undefined method `first' for :foo:Symbol):
+ # CSV::Table.new(:foo)
+ #
+ # Raises an exception if an element of +array_of_rows+ is not a \CSV::Table object:
+ # # Raises NoMethodError (undefined method `headers' for :foo:Symbol):
+ # CSV::Table.new([:foo])
def initialize(array_of_rows, headers: nil)
@table = array_of_rows
@headers = headers
@@ -54,88 +222,141 @@ class CSV
extend Forwardable
def_delegators :@table, :empty?, :length, :size
+ # :call-seq:
+ # table.by_col -> table_dup
#
- # Returns a duplicate table object, in column mode. This is handy for
- # chaining in a single call without changing the table mode, but be aware
- # that this method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in column mode
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # dup_table = table.by_col
+ # dup_table.mode # => :col
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_col['Name']
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_col
self.class.new(@table.dup).by_col!
end
+ # :call-seq:
+ # table.by_col! -> self
#
- # Switches the mode of this table to column mode. All calls to indexing and
- # iteration methods will work with columns until the mode is changed again.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to column mode
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # table1 = table.by_col!
+ # table.mode # => :col
+ # table1.equal?(table) # => true # Returned self
def by_col!
@mode = :col
self
end
+ # :call-seq:
+ # table.by_col_or_row -> table_dup
#
- # Returns a duplicate table object, in mixed mode. This is handy for
- # chaining in a single call without changing the table mode, but be aware
- # that this method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in mixed mode
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true).by_col!
+ # table.mode # => :col
+ # dup_table = table.by_col_or_row
+ # dup_table.mode # => :col_or_row
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_col_or_row['Name']
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_col_or_row
self.class.new(@table.dup).by_col_or_row!
end
+ # :call-seq:
+ # table.by_col_or_row! -> self
#
- # Switches the mode of this table to mixed mode. All calls to indexing and
- # iteration methods will use the default intelligent indexing system until
- # the mode is changed again. In mixed mode an index is assumed to be a row
- # reference while anything else is assumed to be column access by headers.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to mixed mode
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true).by_col!
+ # table.mode # => :col
+ # table1 = table.by_col_or_row!
+ # table.mode # => :col_or_row
+ # table1.equal?(table) # => true # Returned self
def by_col_or_row!
@mode = :col_or_row
self
end
+ # :call-seq:
+ # table.by_row -> table_dup
#
- # Returns a duplicate table object, in row mode. This is handy for chaining
- # in a single call without changing the table mode, but be aware that this
- # method can consume a fair amount of memory for bigger data sets.
+ # Returns a duplicate of +self+, in row mode
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # dup_table = table.by_row
+ # dup_table.mode # => :row
+ # dup_table.equal?(table) # => false # It's a dup
#
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
+ # This may be used to chain method calls without changing the mode
+ # (but also will affect performance and memory usage):
+ # dup_table.by_row[1]
#
+ # Also note that changes to the duplicate table will not affect the original.
def by_row
self.class.new(@table.dup).by_row!
end
+ # :call-seq:
+ # table.by_row! -> self
#
- # Switches the mode of this table to row mode. All calls to indexing and
- # iteration methods will work with rows until the mode is changed again.
- #
- # This method returns the table and is safe to chain.
- #
+ # Sets the mode for +self+ to row mode
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.mode # => :col_or_row
+ # table1 = table.by_row!
+ # table.mode # => :row
+ # table1.equal?(table) # => true # Returned self
def by_row!
@mode = :row
self
end
+ # :call-seq:
+ # table.headers -> array_of_headers
#
- # Returns the headers for the first row of this table (assumed to match all
- # other rows). The headers Array passed to CSV::Table.new is returned for
- # empty tables.
+ # Returns a new \Array containing the \String headers for the table.
#
+ # If the table is not empty, returns the headers from the first row:
+ # rows = [
+ # CSV::Row.new(['Foo', 'Bar'], []),
+ # CSV::Row.new(['FOO', 'BAR'], []),
+ # CSV::Row.new(['foo', 'bar'], []),
+ # ]
+ # table = CSV::Table.new(rows)
+ # table.headers # => ["Foo", "Bar"]
+ # table.delete(0)
+ # table.headers # => ["FOO", "BAR"]
+ # table.delete(0)
+ # table.headers # => ["foo", "bar"]
+ #
+ # If the table is empty, returns a copy of the headers in the table itself:
+ # table.delete(0)
+ # table.headers # => ["Foo", "Bar"]
def headers
if @table.empty?
@headers.dup
@@ -145,17 +366,21 @@ class CSV
end
# :call-seq:
- # table[n] -> row
- # table[range] -> array_of_rows
- # table[header] -> array_of_fields
+ # table[n] -> row or column_data
+ # table[range] -> array_of_rows or array_of_column_data
+ # table[header] -> array_of_column_data
#
# Returns data from the table; does not modify the table.
#
# ---
#
- # The expression <tt>table[n]</tt>, where +n+ is a non-negative \Integer,
- # returns the +n+th row of the table, if that row exists,
- # and if the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>:
+ # Fetch a \Row by Its \Integer Index::
+ # - Form: <tt>table[n]</tt>, +n+ an integer.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: _nth_ row of the table, if that row exists;
+ # otherwise +nil+.
+ #
+ # Returns the _nth_ row of the table if that row exists:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_row! # => #<CSV::Table mode:row row_count:4>
@@ -168,20 +393,45 @@ class CSV
#
# Returns +nil+ if +n+ is too large or too small:
# table[4] # => nil
- # table[-4] => nil
+ # table[-4] # => nil
#
# Raises an exception if the access mode is <tt>:row</tt>
- # and +n+ is not an
- # {Integer-convertible object}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Integer-Convertible+Objects].
+ # and +n+ is not an \Integer:
# table.by_row! # => #<CSV::Table mode:row row_count:4>
# # Raises TypeError (no implicit conversion of String into Integer):
# table['Name']
#
# ---
#
- # The expression <tt>table[range]</tt>, where +range+ is a Range object,
- # returns rows from the table, beginning at row <tt>range.first</tt>,
- # if those rows exist, and if the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>:
+ # Fetch a Column by Its \Integer Index::
+ # - Form: <tt>table[n]</tt>, +n+ an \Integer.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: _nth_ column of the table, if that column exists;
+ # otherwise an \Array of +nil+ fields of length <tt>self.size</tt>.
+ #
+ # Returns the _nth_ column of the table if that column exists:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # table[1] # => ["0", "1", "2"]
+ #
+ # Counts backward from the last column if +n+ is negative:
+ # table[-2] # => ["foo", "bar", "baz"]
+ #
+ # Returns an \Array of +nil+ fields if +n+ is too large or too small:
+ # table[4] # => [nil, nil, nil]
+ # table[-4] # => [nil, nil, nil]
+ #
+ # ---
+ #
+ # Fetch Rows by \Range::
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: rows from the table, beginning at row <tt>range.start</tt>,
+ # if those rows exists.
+ #
+ # Returns rows from the table, beginning at row <tt>range.first</tt>,
+ # if those rows exist:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_row! # => #<CSV::Table mode:row row_count:4>
@@ -191,11 +441,11 @@ class CSV
# rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
#
- # If there are too few rows, returns all from <tt>range.first</tt> to the end:
+ # If there are too few rows, returns all from <tt>range.start</tt> to the end:
# rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1">
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
#
- # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
+ # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
# table[table.size..50] # => []
#
# If <tt>range.end</tt> is negative, calculates the ending index from the end:
@@ -211,9 +461,41 @@ class CSV
#
# ---
#
- # The expression <tt>table[header]</tt>, where +header+ is a \String,
- # returns column values (\Array of \Strings) if the column exists
- # and if the access mode is <tt>:col</tt> or <tt>:col_or_row</tt>:
+ # Fetch Columns by \Range::
+ # - Form: <tt>table[range]</tt>, +range+ a \Range object.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: column data from the table, beginning at column <tt>range.start</tt>,
+ # if those columns exist.
+ #
+ # Returns column values from the table, if the column exists;
+ # the values are arranged by row:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.by_col!
+ # table[0..1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # Special case: if <tt>range.start == headers.size</tt>,
+ # returns an \Array (size: <tt>table.size</tt>) of empty \Arrays:
+ # table[table.headers.size..50] # => [[], [], []]
+ #
+ # If <tt>range.end</tt> is negative, calculates the ending index from the end:
+ # table[0..-1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # If <tt>range.start</tt> is negative, calculates the starting index from the end:
+ # table[-2..2] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
+ #
+ # If <tt>range.start</tt> is larger than <tt>table.size</tt>,
+ # returns an \Array of +nil+ values:
+ # table[4..4] # => [nil, nil, nil]
+ #
+ # ---
+ #
+ # Fetch a Column by Its \String Header::
+ # - Form: <tt>table[header]</tt>, +header+ a \String header.
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>
+ # - Return value: column data from the table, if that +header+ exists.
+ #
+ # Returns column values from the table, if the column exists:
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
# table = CSV.parse(source, headers: true)
# table.by_col! # => #<CSV::Table mode:col row_count:4>
@@ -238,22 +520,132 @@ class CSV
end
end
+ # :call-seq:
+ # table[n] = row -> row
+ # table[n] = field_or_array_of_fields -> field_or_array_of_fields
+ # table[header] = field_or_array_of_fields -> field_or_array_of_fields
#
- # In the default mixed mode, this method assigns rows for index access and
- # columns for header access. You can force the index association by first
- # calling by_col!() or by_row!().
+ # Puts data onto the table.
#
- # Rows may be set to an Array of values (which will inherit the table's
- # headers()) or a CSV::Row.
+ # ---
+ #
+ # Set a \Row by Its \Integer Index::
+ # - Form: <tt>table[n] = row</tt>, +n+ an \Integer,
+ # +row+ a \CSV::Row instance or an \Array of fields.
+ # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
+ # - Return value: +row+.
+ #
+ # If the row exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_row = CSV::Row.new(['Name', 'Value'], ['bat', 3])
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # return_value = table[0] = new_row
+ # return_value.equal?(new_row) # => true # Returned the row
+ # table[0].to_h # => {"Name"=>"bat", "Value"=>3}
+ #
+ # With access mode <tt>:col_or_row</tt>:
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
+ # table[0] = CSV::Row.new(['Name', 'Value'], ['bam', 4])
+ # table[0].to_h # => {"Name"=>"bam", "Value"=>4}
+ #
+ # With an \Array instead of a \CSV::Row, inherits headers from the table:
+ # array = ['bad', 5]
+ # return_value = table[0] = array
+ # return_value.equal?(array) # => true # Returned the array
+ # table[0].to_h # => {"Name"=>"bad", "Value"=>5}
#
- # Columns may be set to a single value, which is copied to each row of the
- # column, or an Array of values. Arrays of values are assigned to rows top
- # to bottom in row major order. Excess values are ignored and if the Array
- # does not have a value for each row the extra rows will receive a +nil+.
+ # If the row does not exist, extends the table by adding rows:
+ # assigns rows with +nil+ as needed:
+ # table.size # => 3
+ # table[5] = ['bag', 6]
+ # table.size # => 6
+ # table[3] # => nil
+ # table[4]# => nil
+ # table[5].to_h # => {"Name"=>"bag", "Value"=>6}
+ #
+ # Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
#
- # Assigning to an existing column or row clobbers the data. Assigning to
- # new columns creates them at the right end of the table.
+ # ---
#
+ # Set a Column by Its \Integer Index::
+ # - Form: <tt>table[n] = array_of_fields</tt>, +n+ an \Integer,
+ # +array_of_fields+ an \Array of \String fields.
+ # - Access mode: <tt>:col</tt>.
+ # - Return value: +array_of_fields+.
+ #
+ # If the column exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_col = [3, 4, 5]
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # return_value = table[1] = new_col
+ # return_value.equal?(new_col) # => true # Returned the column
+ # table[1] # => [3, 4, 5]
+ # # The rows, as revised:
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # If there are too few values, fills with +nil+ values:
+ # table[1] = [0]
+ # table[1] # => [0, nil, nil]
+ #
+ # If there are too many values, ignores the extra values:
+ # table[1] = [0, 1, 2, 3, 4]
+ # table[1] # => [0, 1, 2]
+ #
+ # If a single value is given, replaces all fields in the column with that value:
+ # table[1] = 'bat'
+ # table[1] # => ["bat", "bat", "bat"]
+ #
+ # ---
+ #
+ # Set a Column by Its \String Header::
+ # - Form: <tt>table[header] = field_or_array_of_fields</tt>,
+ # +header+ a \String header, +field_or_array_of_fields+ a field value
+ # or an \Array of \String fields.
+ # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>.
+ # - Return value: +field_or_array_of_fields+.
+ #
+ # If the column exists, it is replaced:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # new_col = [3, 4, 5]
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ # return_value = table['Value'] = new_col
+ # return_value.equal?(new_col) # => true # Returned the column
+ # table['Value'] # => [3, 4, 5]
+ # # The rows, as revised:
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
+ #
+ # If there are too few values, fills with +nil+ values:
+ # table['Value'] = [0]
+ # table['Value'] # => [0, nil, nil]
+ #
+ # If there are too many values, ignores the extra values:
+ # table['Value'] = [0, 1, 2, 3, 4]
+ # table['Value'] # => [0, 1, 2]
+ #
+ # If the column does not exist, extends the table by adding columns:
+ # table['Note'] = ['x', 'y', 'z']
+ # table['Note'] # => ["x", "y", "z"]
+ # # The rows, as revised:
+ # table.by_row!
+ # table[0].to_h # => {"Name"=>"foo", "Value"=>0, "Note"=>"x"}
+ # table[1].to_h # => {"Name"=>"bar", "Value"=>1, "Note"=>"y"}
+ # table[2].to_h # => {"Name"=>"baz", "Value"=>2, "Note"=>"z"}
+ # table.by_col!
+ #
+ # If a single value is given, replaces all fields in the column with that value:
+ # table['Value'] = 'bat'
+ # table['Value'] # => ["bat", "bat", "bat"]
def []=(index_or_header, value)
if @mode == :row or # by index
(@mode == :col_or_row and index_or_header.is_a? Integer)
@@ -463,6 +855,9 @@ class CSV
end
end
+ # :call-seq:
+ # table.delete_if {|row_or_column| ... } -> self
+ #
# Removes rows or columns for which the block returns a truthy value;
# returns +self+.
#
@@ -495,9 +890,8 @@ class CSV
if @mode == :row or @mode == :col_or_row # by index
@table.delete_if(&block)
else # by header
- deleted = []
headers.each do |header|
- deleted << delete(header) if yield([header, self[header]])
+ delete(header) if yield([header, self[header]])
end
end
@@ -506,6 +900,9 @@ class CSV
include Enumerable
+ # :call-seq:
+ # table.each {|row_or_column| ... ) -> self
+ #
# Calls the block with each row or column; returns +self+.
#
# When the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
@@ -534,7 +931,9 @@ class CSV
return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
if @mode == :col
- headers.each { |header| yield([header, self[header]]) }
+ headers.each.with_index do |header, i|
+ yield([header, @table.map {|row| row[header, i]}])
+ end
else
@table.each(&block)
end
@@ -542,6 +941,9 @@ class CSV
self # for chaining
end
+ # :call-seq:
+ # table == other_table -> true or false
+ #
# Returns +true+ if all each row of +self+ <tt>==</tt>
# the corresponding row of +other_table+, otherwise, +false+.
#
@@ -565,10 +967,14 @@ class CSV
@table == other
end
+ # :call-seq:
+ # table.to_a -> array_of_arrays
#
- # Returns the table as an Array of Arrays. Headers will be the first row,
- # then all of the field rows will follow.
- #
+ # Returns the table as an \Array of \Arrays;
+ # the headers are in the first row:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
def to_a
array = [headers]
@table.each do |row|
@@ -578,16 +984,29 @@ class CSV
array
end
+ # :call-seq:
+ # table.to_csv(**options) -> csv_string
#
- # Returns the table as a complete CSV String. Headers will be listed first,
- # then all of the field rows.
+ # Returns the table as \CSV string.
+ # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
#
- # This method assumes you want the Table.headers(), unless you explicitly
- # pass <tt>:write_headers => false</tt>.
+ # Defaults option +write_headers+ to +true+:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
#
- def to_csv(write_headers: true, **options)
+ # Omits the headers if option +write_headers+ is given as +false+
+ # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
+ # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
+ #
+ # Limit rows if option +limit+ is given like +2+:
+ # table.to_csv(limit: 2) # => "Name,Value\nfoo,0\nbar,1\n"
+ def to_csv(write_headers: true, limit: nil, **options)
array = write_headers ? [headers.to_csv(**options)] : []
- @table.each do |row|
+ limit ||= @table.size
+ limit = @table.size + 1 + limit if limit < 0
+ limit = 0 if limit < 0
+ @table.first(limit).each do |row|
array.push(row.fields.to_csv(**options)) unless row.header_row?
end
@@ -613,9 +1032,24 @@ class CSV
end
end
- # Shows the mode and size of this table in a US-ASCII String.
+ # :call-seq:
+ # table.inspect => string
+ #
+ # Returns a <tt>US-ASCII</tt>-encoded \String showing table:
+ # - Class: <tt>CSV::Table</tt>.
+ # - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
+ # - Size: Row count, including the header row.
+ #
+ # Example:
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
+ # table = CSV.parse(source, headers: true)
+ # table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>\nName,Value\nfoo,0\nbar,1\nbaz,2\n"
+ #
def inspect
- "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
+ inspected = +"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
+ summary = to_csv(limit: 5)
+ inspected << "\n" << summary if summary.encoding.ascii_compatible?
+ inspected
end
end
end
diff --git a/lib/csv/version.rb b/lib/csv/version.rb
index d1d0dc0e02..e05d63d801 100644
--- a/lib/csv/version.rb
+++ b/lib/csv/version.rb
@@ -2,5 +2,5 @@
class CSV
# The version of the installed library.
- VERSION = "3.2.2"
+ VERSION = "3.2.6"
end
diff --git a/lib/csv/writer.rb b/lib/csv/writer.rb
index 4a9a35c5af..030a295bc9 100644
--- a/lib/csv/writer.rb
+++ b/lib/csv/writer.rb
@@ -1,11 +1,8 @@
# frozen_string_literal: true
require_relative "input_record_separator"
-require_relative "match_p"
require_relative "row"
-using CSV::MatchP if CSV.const_defined?(:MatchP)
-
class CSV
# Note: Don't use this class directly. This is an internal class.
class Writer
@@ -42,7 +39,10 @@ class CSV
@headers ||= row if @use_headers
@lineno += 1
- row = @fields_converter.convert(row, nil, lineno) if @fields_converter
+ if @fields_converter
+ quoted_fields = [false] * row.size
+ row = @fields_converter.convert(row, nil, lineno, quoted_fields)
+ end
i = -1
converted_row = row.collect do |field|
@@ -97,7 +97,7 @@ class CSV
return unless @headers
converter = @options[:header_fields_converter]
- @headers = converter.convert(@headers, nil, 0)
+ @headers = converter.convert(@headers, nil, 0, [])
@headers.each do |header|
header.freeze if header.is_a?(String)
end
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 70d4e4ad1d..387a5f063d 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -39,7 +39,7 @@
# Be advised, RDoc will not detect delegated methods.
#
class Delegator < BasicObject
- VERSION = "0.2.0"
+ VERSION = "0.3.0"
kernel = ::Kernel.dup
kernel.class_eval do
diff --git a/lib/did_you_mean/spell_checkers/method_name_checker.rb b/lib/did_you_mean/spell_checkers/method_name_checker.rb
index d8ebaa4616..b5cbbb5da6 100644
--- a/lib/did_you_mean/spell_checkers/method_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/method_name_checker.rb
@@ -59,6 +59,13 @@ module DidYouMean
method_names = receiver.methods + receiver.singleton_methods
method_names += receiver.private_methods if @private_call
method_names.uniq!
+ # Assume that people trying to use a writer are not interested in a reader
+ # and vice versa
+ if method_name.match?(/=\Z/)
+ method_names.select! { |name| name.match?(/=\Z/) }
+ else
+ method_names.reject! { |name| name.match?(/=\Z/) }
+ end
method_names
else
[]
diff --git a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
index 36d00349c6..9a6e04fe64 100644
--- a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
@@ -79,7 +79,7 @@ module DidYouMean
def corrections
@corrections ||= SpellChecker
.new(dictionary: (RB_RESERVED_WORDS + lvar_names + method_names + ivar_names + cvar_names))
- .correct(name) - NAMES_TO_EXCLUDE[@name]
+ .correct(name).uniq - NAMES_TO_EXCLUDE[@name]
end
end
end
diff --git a/lib/did_you_mean/version.rb b/lib/did_you_mean/version.rb
index b5fe50b5ed..5745ca1efd 100644
--- a/lib/did_you_mean/version.rb
+++ b/lib/did_you_mean/version.rb
@@ -1,3 +1,3 @@
module DidYouMean
- VERSION = "1.6.1".freeze
+ VERSION = "1.6.3".freeze
end
diff --git a/lib/drb/version.rb b/lib/drb/version.rb
index efaccf0319..10d33445b6 100644
--- a/lib/drb/version.rb
+++ b/lib/drb/version.rb
@@ -1,3 +1,3 @@
module DRb
- VERSION = "2.1.0"
+ VERSION = "2.1.1"
end
diff --git a/lib/erb.gemspec b/lib/erb.gemspec
index 43ffc89c69..d973cc10de 100644
--- a/lib/erb.gemspec
+++ b/lib/erb.gemspec
@@ -8,8 +8,8 @@ end
Gem::Specification.new do |spec|
spec.name = 'erb'
spec.version = ERB.const_get(:VERSION, false)
- spec.authors = ['Masatoshi SEKI']
- spec.email = ['seki@ruby-lang.org']
+ spec.authors = ['Masatoshi SEKI', 'Takashi Kokubun']
+ spec.email = ['seki@ruby-lang.org', 'takashikkbn@gmail.com']
spec.summary = %q{An easy to use but powerful templating system for Ruby.}
spec.description = %q{An easy to use but powerful templating system for Ruby.}
@@ -27,5 +27,12 @@ Gem::Specification.new do |spec|
spec.executables = ['erb']
spec.require_paths = ['lib']
- spec.add_dependency 'cgi'
+ if RUBY_ENGINE == 'jruby'
+ spec.platform = 'java'
+ else
+ spec.required_ruby_version = '>= 2.7.0'
+ spec.extensions = ['ext/erb/escape/extconf.rb']
+ end
+
+ spec.add_dependency 'cgi', '>= 0.3.3'
end
diff --git a/lib/erb.rb b/lib/erb.rb
index 0e42425a60..754419f819 100644
--- a/lib/erb.rb
+++ b/lib/erb.rb
@@ -14,6 +14,9 @@
require 'cgi/util'
require 'erb/version'
+require 'erb/compiler'
+require 'erb/def_method'
+require 'erb/util'
#
# = ERB -- Ruby Templating
@@ -264,486 +267,7 @@ class ERB
def self.version
VERSION
end
-end
-#--
-# ERB::Compiler
-class ERB
- # = ERB::Compiler
- #
- # Compiles ERB templates into Ruby code; the compiled code produces the
- # template result when evaluated. ERB::Compiler provides hooks to define how
- # generated output is handled.
- #
- # Internally ERB does something like this to generate the code returned by
- # ERB#src:
- #
- # compiler = ERB::Compiler.new('<>')
- # compiler.pre_cmd = ["_erbout=+''"]
- # compiler.put_cmd = "_erbout.<<"
- # compiler.insert_cmd = "_erbout.<<"
- # compiler.post_cmd = ["_erbout"]
- #
- # code, enc = compiler.compile("Got <%= obj %>!\n")
- # puts code
- #
- # <i>Generates</i>:
- #
- # #coding:UTF-8
- # _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
- #
- # By default the output is sent to the print method. For example:
- #
- # compiler = ERB::Compiler.new('<>')
- # code, enc = compiler.compile("Got <%= obj %>!\n")
- # puts code
- #
- # <i>Generates</i>:
- #
- # #coding:UTF-8
- # print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
- #
- # == Evaluation
- #
- # The compiled code can be used in any context where the names in the code
- # correctly resolve. Using the last example, each of these print 'Got It!'
- #
- # Evaluate using a variable:
- #
- # obj = 'It'
- # eval code
- #
- # Evaluate using an input:
- #
- # mod = Module.new
- # mod.module_eval %{
- # def get(obj)
- # #{code}
- # end
- # }
- # extend mod
- # get('It')
- #
- # Evaluate using an accessor:
- #
- # klass = Class.new Object
- # klass.class_eval %{
- # attr_accessor :obj
- # def initialize(obj)
- # @obj = obj
- # end
- # def get_it
- # #{code}
- # end
- # }
- # klass.new('It').get_it
- #
- # Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
- class Compiler # :nodoc:
- class PercentLine # :nodoc:
- def initialize(str)
- @value = str
- end
- attr_reader :value
- alias :to_s :value
- end
-
- class Scanner # :nodoc:
- @scanner_map = {}
- class << self
- def register_scanner(klass, trim_mode, percent)
- @scanner_map[[trim_mode, percent]] = klass
- end
- alias :regist_scanner :register_scanner
- end
-
- def self.default_scanner=(klass)
- @default_scanner = klass
- end
-
- def self.make_scanner(src, trim_mode, percent)
- klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
- klass.new(src, trim_mode, percent)
- end
-
- DEFAULT_STAGS = %w(<%% <%= <%# <%).freeze
- DEFAULT_ETAGS = %w(%%> %>).freeze
- def initialize(src, trim_mode, percent)
- @src = src
- @stag = nil
- @stags = DEFAULT_STAGS
- @etags = DEFAULT_ETAGS
- end
- attr_accessor :stag
- attr_reader :stags, :etags
-
- def scan; end
- end
-
- class TrimScanner < Scanner # :nodoc:
- def initialize(src, trim_mode, percent)
- super
- @trim_mode = trim_mode
- @percent = percent
- if @trim_mode == '>'
- @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:trim_line1)
- elsif @trim_mode == '<>'
- @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:trim_line2)
- elsif @trim_mode == '-'
- @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\r?\n|-%>|#{(stags + etags).join('|')}|\z)/m
- @scan_line = self.method(:explicit_trim_line)
- else
- @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
- @scan_line = self.method(:scan_line)
- end
- end
-
- def scan(&block)
- @stag = nil
- if @percent
- @src.each_line do |line|
- percent_line(line, &block)
- end
- else
- @scan_line.call(@src, &block)
- end
- nil
- end
-
- def percent_line(line, &block)
- if @stag || line[0] != ?%
- return @scan_line.call(line, &block)
- end
-
- line[0] = ''
- if line[0] == ?%
- @scan_line.call(line, &block)
- else
- yield(PercentLine.new(line.chomp))
- end
- end
-
- def scan_line(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- yield(token)
- end
- end
- end
-
- def trim_line1(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- if token == "%>\n" || token == "%>\r\n"
- yield('%>')
- yield(:cr)
- else
- yield(token)
- end
- end
- end
- end
-
- def trim_line2(line)
- head = nil
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- head = token unless head
- if token == "%>\n" || token == "%>\r\n"
- yield('%>')
- if is_erb_stag?(head)
- yield(:cr)
- else
- yield("\n")
- end
- head = nil
- else
- yield(token)
- head = nil if token == "\n"
- end
- end
- end
- end
-
- def explicit_trim_line(line)
- line.scan(@scan_reg) do |tokens|
- tokens.each do |token|
- next if token.empty?
- if @stag.nil? && /[ \t]*<%-/ =~ token
- yield('<%')
- elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
- yield('%>')
- yield(:cr)
- elsif @stag && token == '-%>'
- yield('%>')
- else
- yield(token)
- end
- end
- end
- end
-
- ERB_STAG = %w(<%= <%# <%)
- def is_erb_stag?(s)
- ERB_STAG.member?(s)
- end
- end
-
- Scanner.default_scanner = TrimScanner
-
- begin
- require 'strscan'
- rescue LoadError
- else
- class SimpleScanner < Scanner # :nodoc:
- def scan
- stag_reg = (stags == DEFAULT_STAGS) ? /(.*?)(<%[%=#]?|\z)/m : /(.*?)(#{stags.join('|')}|\z)/m
- etag_reg = (etags == DEFAULT_ETAGS) ? /(.*?)(%%?>|\z)/m : /(.*?)(#{etags.join('|')}|\z)/m
- scanner = StringScanner.new(@src)
- while ! scanner.eos?
- scanner.scan(@stag ? etag_reg : stag_reg)
- yield(scanner[1])
- yield(scanner[2])
- end
- end
- end
- Scanner.register_scanner(SimpleScanner, nil, false)
-
- class ExplicitScanner < Scanner # :nodoc:
- def scan
- stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
- etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
- scanner = StringScanner.new(@src)
- while ! scanner.eos?
- scanner.scan(@stag ? etag_reg : stag_reg)
- yield(scanner[1])
-
- elem = scanner[2]
- if /[ \t]*<%-/ =~ elem
- yield('<%')
- elsif elem == '-%>'
- yield('%>')
- yield(:cr) if scanner.scan(/(\r?\n|\z)/)
- else
- yield(elem)
- end
- end
- end
- end
- Scanner.register_scanner(ExplicitScanner, '-', false)
- end
-
- class Buffer # :nodoc:
- def initialize(compiler, enc=nil, frozen=nil)
- @compiler = compiler
- @line = []
- @script = +''
- @script << "#coding:#{enc}\n" if enc
- @script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
- @compiler.pre_cmd.each do |x|
- push(x)
- end
- end
- attr_reader :script
-
- def push(cmd)
- @line << cmd
- end
-
- def cr
- @script << (@line.join('; '))
- @line = []
- @script << "\n"
- end
-
- def close
- return unless @line
- @compiler.post_cmd.each do |x|
- push(x)
- end
- @script << (@line.join('; '))
- @line = nil
- end
- end
-
- def add_put_cmd(out, content)
- out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
- end
-
- def add_insert_cmd(out, content)
- out.push("#{@insert_cmd}((#{content}).to_s)")
- end
-
- # Compiles an ERB template into Ruby code. Returns an array of the code
- # and encoding like ["code", Encoding].
- def compile(s)
- enc = s.encoding
- raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
- s = s.b # see String#b
- magic_comment = detect_magic_comment(s, enc)
- out = Buffer.new(self, *magic_comment)
-
- self.content = +''
- scanner = make_scanner(s)
- scanner.scan do |token|
- next if token.nil?
- next if token == ''
- if scanner.stag.nil?
- compile_stag(token, out, scanner)
- else
- compile_etag(token, out, scanner)
- end
- end
- add_put_cmd(out, content) if content.size > 0
- out.close
- return out.script, *magic_comment
- end
-
- def compile_stag(stag, out, scanner)
- case stag
- when PercentLine
- add_put_cmd(out, content) if content.size > 0
- self.content = +''
- out.push(stag.to_s)
- out.cr
- when :cr
- out.cr
- when '<%', '<%=', '<%#'
- scanner.stag = stag
- add_put_cmd(out, content) if content.size > 0
- self.content = +''
- when "\n"
- content << "\n"
- add_put_cmd(out, content)
- self.content = +''
- when '<%%'
- content << '<%'
- else
- content << stag
- end
- end
-
- def compile_etag(etag, out, scanner)
- case etag
- when '%>'
- compile_content(scanner.stag, out)
- scanner.stag = nil
- self.content = +''
- when '%%>'
- content << '%>'
- else
- content << etag
- end
- end
-
- def compile_content(stag, out)
- case stag
- when '<%'
- if content[-1] == ?\n
- content.chop!
- out.push(content)
- out.cr
- else
- out.push(content)
- end
- when '<%='
- add_insert_cmd(out, content)
- when '<%#'
- # commented out
- end
- end
-
- def prepare_trim_mode(mode) # :nodoc:
- case mode
- when 1
- return [false, '>']
- when 2
- return [false, '<>']
- when 0, nil
- return [false, nil]
- when String
- unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
- warn_invalid_trim_mode(mode, uplevel: 5)
- end
-
- perc = mode.include?('%')
- if mode.include?('-')
- return [perc, '-']
- elsif mode.include?('<>')
- return [perc, '<>']
- elsif mode.include?('>')
- return [perc, '>']
- else
- [perc, nil]
- end
- else
- warn_invalid_trim_mode(mode, uplevel: 5)
- return [false, nil]
- end
- end
-
- def make_scanner(src) # :nodoc:
- Scanner.make_scanner(src, @trim_mode, @percent)
- end
-
- # Construct a new compiler using the trim_mode. See ERB::new for available
- # trim modes.
- def initialize(trim_mode)
- @percent, @trim_mode = prepare_trim_mode(trim_mode)
- @put_cmd = 'print'
- @insert_cmd = @put_cmd
- @pre_cmd = []
- @post_cmd = []
- end
- attr_reader :percent, :trim_mode
-
- # The command to handle text that ends with a newline
- attr_accessor :put_cmd
-
- # The command to handle text that is inserted prior to a newline
- attr_accessor :insert_cmd
-
- # An array of commands prepended to compiled code
- attr_accessor :pre_cmd
-
- # An array of commands appended to compiled code
- attr_accessor :post_cmd
-
- private
-
- # A buffered text in #compile
- attr_accessor :content
-
- def detect_magic_comment(s, enc = nil)
- re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
- frozen = nil
- s.scan(re) do
- comment = $+
- comment = $1 if comment[/-\*-\s*([^\s].*?)\s*-\*-$/]
- case comment
- when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
- enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
- when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
- frozen = $1
- end
- end
- return enc, frozen
- end
-
- def warn_invalid_trim_mode(mode, uplevel:)
- warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
- end
- end
-end
-
-#--
-# ERB
-class ERB
#
# Constructs a new ERB object with the template specified in _str_.
#
@@ -980,100 +504,3 @@ class ERB
cls
end
end
-
-#--
-# ERB::Util
-class ERB
- # A utility module for conversion routines, often handy in HTML generation.
- module Util
- public
- #
- # A utility method for escaping HTML tag characters in _s_.
- #
- # require "erb"
- # include ERB::Util
- #
- # puts html_escape("is a > 0 & a < 10?")
- #
- # _Generates_
- #
- # is a &gt; 0 &amp; a &lt; 10?
- #
- def html_escape(s)
- CGI.escapeHTML(s.to_s)
- end
- alias h html_escape
- module_function :h
- module_function :html_escape
-
- #
- # A utility method for encoding the String _s_ as a URL.
- #
- # require "erb"
- # include ERB::Util
- #
- # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
- #
- # _Generates_
- #
- # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
- #
- def url_encode(s)
- s.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m|
- sprintf("%%%02X", m.unpack1("C"))
- }
- end
- alias u url_encode
- module_function :u
- module_function :url_encode
- end
-end
-
-#--
-# ERB::DefMethod
-class ERB
- # Utility module to define eRuby script as instance method.
- #
- # === Example
- #
- # example.rhtml:
- # <% for item in @items %>
- # <b><%= item %></b>
- # <% end %>
- #
- # example.rb:
- # require 'erb'
- # class MyClass
- # extend ERB::DefMethod
- # def_erb_method('render()', 'example.rhtml')
- # def initialize(items)
- # @items = items
- # end
- # end
- # print MyClass.new([10,20,30]).render()
- #
- # result:
- #
- # <b>10</b>
- #
- # <b>20</b>
- #
- # <b>30</b>
- #
- module DefMethod
- public
- # define _methodname_ as instance method of current module, using ERB
- # object or eRuby file
- def def_erb_method(methodname, erb_or_fname)
- if erb_or_fname.kind_of? String
- fname = erb_or_fname
- erb = ERB.new(File.read(fname))
- erb.def_method(self, methodname, fname)
- else
- erb = erb_or_fname
- erb.def_method(self, methodname, erb.filename || '(ERB)')
- end
- end
- module_function :def_erb_method
- end
-end
diff --git a/lib/erb/compiler.rb b/lib/erb/compiler.rb
new file mode 100644
index 0000000000..547d2c4c44
--- /dev/null
+++ b/lib/erb/compiler.rb
@@ -0,0 +1,471 @@
+#--
+# ERB::Compiler
+#
+# Compiles ERB templates into Ruby code; the compiled code produces the
+# template result when evaluated. ERB::Compiler provides hooks to define how
+# generated output is handled.
+#
+# Internally ERB does something like this to generate the code returned by
+# ERB#src:
+#
+# compiler = ERB::Compiler.new('<>')
+# compiler.pre_cmd = ["_erbout=+''"]
+# compiler.put_cmd = "_erbout.<<"
+# compiler.insert_cmd = "_erbout.<<"
+# compiler.post_cmd = ["_erbout"]
+#
+# code, enc = compiler.compile("Got <%= obj %>!\n")
+# puts code
+#
+# <i>Generates</i>:
+#
+# #coding:UTF-8
+# _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
+#
+# By default the output is sent to the print method. For example:
+#
+# compiler = ERB::Compiler.new('<>')
+# code, enc = compiler.compile("Got <%= obj %>!\n")
+# puts code
+#
+# <i>Generates</i>:
+#
+# #coding:UTF-8
+# print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
+#
+# == Evaluation
+#
+# The compiled code can be used in any context where the names in the code
+# correctly resolve. Using the last example, each of these print 'Got It!'
+#
+# Evaluate using a variable:
+#
+# obj = 'It'
+# eval code
+#
+# Evaluate using an input:
+#
+# mod = Module.new
+# mod.module_eval %{
+# def get(obj)
+# #{code}
+# end
+# }
+# extend mod
+# get('It')
+#
+# Evaluate using an accessor:
+#
+# klass = Class.new Object
+# klass.class_eval %{
+# attr_accessor :obj
+# def initialize(obj)
+# @obj = obj
+# end
+# def get_it
+# #{code}
+# end
+# }
+# klass.new('It').get_it
+#
+# Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
+class ERB::Compiler # :nodoc:
+ class PercentLine # :nodoc:
+ def initialize(str)
+ @value = str
+ end
+ attr_reader :value
+ alias :to_s :value
+ end
+
+ class Scanner # :nodoc:
+ @scanner_map = {}
+ class << self
+ def register_scanner(klass, trim_mode, percent)
+ @scanner_map[[trim_mode, percent]] = klass
+ end
+ alias :regist_scanner :register_scanner
+ end
+
+ def self.default_scanner=(klass)
+ @default_scanner = klass
+ end
+
+ def self.make_scanner(src, trim_mode, percent)
+ klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
+ klass.new(src, trim_mode, percent)
+ end
+
+ DEFAULT_STAGS = %w(<%% <%= <%# <%).freeze
+ DEFAULT_ETAGS = %w(%%> %>).freeze
+ def initialize(src, trim_mode, percent)
+ @src = src
+ @stag = nil
+ @stags = DEFAULT_STAGS
+ @etags = DEFAULT_ETAGS
+ end
+ attr_accessor :stag
+ attr_reader :stags, :etags
+
+ def scan; end
+ end
+
+ class TrimScanner < Scanner # :nodoc:
+ def initialize(src, trim_mode, percent)
+ super
+ @trim_mode = trim_mode
+ @percent = percent
+ if @trim_mode == '>'
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:trim_line1)
+ elsif @trim_mode == '<>'
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:trim_line2)
+ elsif @trim_mode == '-'
+ @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\r?\n|-%>|#{(stags + etags).join('|')}|\z)/m
+ @scan_line = self.method(:explicit_trim_line)
+ else
+ @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_line = self.method(:scan_line)
+ end
+ end
+
+ def scan(&block)
+ @stag = nil
+ if @percent
+ @src.each_line do |line|
+ percent_line(line, &block)
+ end
+ else
+ @scan_line.call(@src, &block)
+ end
+ nil
+ end
+
+ def percent_line(line, &block)
+ if @stag || line[0] != ?%
+ return @scan_line.call(line, &block)
+ end
+
+ line[0] = ''
+ if line[0] == ?%
+ @scan_line.call(line, &block)
+ else
+ yield(PercentLine.new(line.chomp))
+ end
+ end
+
+ def scan_line(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ yield(token)
+ end
+ end
+ end
+
+ def trim_line1(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if token == "%>\n" || token == "%>\r\n"
+ yield('%>')
+ yield(:cr)
+ else
+ yield(token)
+ end
+ end
+ end
+ end
+
+ def trim_line2(line)
+ head = nil
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ head = token unless head
+ if token == "%>\n" || token == "%>\r\n"
+ yield('%>')
+ if is_erb_stag?(head)
+ yield(:cr)
+ else
+ yield("\n")
+ end
+ head = nil
+ else
+ yield(token)
+ head = nil if token == "\n"
+ end
+ end
+ end
+ end
+
+ def explicit_trim_line(line)
+ line.scan(@scan_reg) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if @stag.nil? && /[ \t]*<%-/ =~ token
+ yield('<%')
+ elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
+ yield('%>')
+ yield(:cr)
+ elsif @stag && token == '-%>'
+ yield('%>')
+ else
+ yield(token)
+ end
+ end
+ end
+ end
+
+ ERB_STAG = %w(<%= <%# <%)
+ def is_erb_stag?(s)
+ ERB_STAG.member?(s)
+ end
+ end
+
+ Scanner.default_scanner = TrimScanner
+
+ begin
+ require 'strscan'
+ rescue LoadError
+ else
+ class SimpleScanner < Scanner # :nodoc:
+ def scan
+ stag_reg = (stags == DEFAULT_STAGS) ? /(.*?)(<%[%=#]?|\z)/m : /(.*?)(#{stags.join('|')}|\z)/m
+ etag_reg = (etags == DEFAULT_ETAGS) ? /(.*?)(%%?>|\z)/m : /(.*?)(#{etags.join('|')}|\z)/m
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ yield(scanner[1])
+ yield(scanner[2])
+ end
+ end
+ end
+ Scanner.register_scanner(SimpleScanner, nil, false)
+
+ class ExplicitScanner < Scanner # :nodoc:
+ def scan
+ stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
+ etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ yield(scanner[1])
+
+ elem = scanner[2]
+ if /[ \t]*<%-/ =~ elem
+ yield('<%')
+ elsif elem == '-%>'
+ yield('%>')
+ yield(:cr) if scanner.scan(/(\r?\n|\z)/)
+ else
+ yield(elem)
+ end
+ end
+ end
+ end
+ Scanner.register_scanner(ExplicitScanner, '-', false)
+ end
+
+ class Buffer # :nodoc:
+ def initialize(compiler, enc=nil, frozen=nil)
+ @compiler = compiler
+ @line = []
+ @script = +''
+ @script << "#coding:#{enc}\n" if enc
+ @script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
+ @compiler.pre_cmd.each do |x|
+ push(x)
+ end
+ end
+ attr_reader :script
+
+ def push(cmd)
+ @line << cmd
+ end
+
+ def cr
+ @script << (@line.join('; '))
+ @line = []
+ @script << "\n"
+ end
+
+ def close
+ return unless @line
+ @compiler.post_cmd.each do |x|
+ push(x)
+ end
+ @script << (@line.join('; '))
+ @line = nil
+ end
+ end
+
+ def add_put_cmd(out, content)
+ out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
+ end
+
+ def add_insert_cmd(out, content)
+ out.push("#{@insert_cmd}((#{content}).to_s)")
+ end
+
+ # Compiles an ERB template into Ruby code. Returns an array of the code
+ # and encoding like ["code", Encoding].
+ def compile(s)
+ enc = s.encoding
+ raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
+ s = s.b # see String#b
+ magic_comment = detect_magic_comment(s, enc)
+ out = Buffer.new(self, *magic_comment)
+
+ self.content = +''
+ scanner = make_scanner(s)
+ scanner.scan do |token|
+ next if token.nil?
+ next if token == ''
+ if scanner.stag.nil?
+ compile_stag(token, out, scanner)
+ else
+ compile_etag(token, out, scanner)
+ end
+ end
+ add_put_cmd(out, content) if content.size > 0
+ out.close
+ return out.script, *magic_comment
+ end
+
+ def compile_stag(stag, out, scanner)
+ case stag
+ when PercentLine
+ add_put_cmd(out, content) if content.size > 0
+ self.content = +''
+ out.push(stag.to_s)
+ out.cr
+ when :cr
+ out.cr
+ when '<%', '<%=', '<%#'
+ scanner.stag = stag
+ add_put_cmd(out, content) if content.size > 0
+ self.content = +''
+ when "\n"
+ content << "\n"
+ add_put_cmd(out, content)
+ self.content = +''
+ when '<%%'
+ content << '<%'
+ else
+ content << stag
+ end
+ end
+
+ def compile_etag(etag, out, scanner)
+ case etag
+ when '%>'
+ compile_content(scanner.stag, out)
+ scanner.stag = nil
+ self.content = +''
+ when '%%>'
+ content << '%>'
+ else
+ content << etag
+ end
+ end
+
+ def compile_content(stag, out)
+ case stag
+ when '<%'
+ if content[-1] == ?\n
+ content.chop!
+ out.push(content)
+ out.cr
+ else
+ out.push(content)
+ end
+ when '<%='
+ add_insert_cmd(out, content)
+ when '<%#'
+ out.push("\n" * content.count("\n")) # only adjust lineno
+ end
+ end
+
+ def prepare_trim_mode(mode) # :nodoc:
+ case mode
+ when 1
+ return [false, '>']
+ when 2
+ return [false, '<>']
+ when 0, nil
+ return [false, nil]
+ when String
+ unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
+ warn_invalid_trim_mode(mode, uplevel: 5)
+ end
+
+ perc = mode.include?('%')
+ if mode.include?('-')
+ return [perc, '-']
+ elsif mode.include?('<>')
+ return [perc, '<>']
+ elsif mode.include?('>')
+ return [perc, '>']
+ else
+ [perc, nil]
+ end
+ else
+ warn_invalid_trim_mode(mode, uplevel: 5)
+ return [false, nil]
+ end
+ end
+
+ def make_scanner(src) # :nodoc:
+ Scanner.make_scanner(src, @trim_mode, @percent)
+ end
+
+ # Construct a new compiler using the trim_mode. See ERB::new for available
+ # trim modes.
+ def initialize(trim_mode)
+ @percent, @trim_mode = prepare_trim_mode(trim_mode)
+ @put_cmd = 'print'
+ @insert_cmd = @put_cmd
+ @pre_cmd = []
+ @post_cmd = []
+ end
+ attr_reader :percent, :trim_mode
+
+ # The command to handle text that ends with a newline
+ attr_accessor :put_cmd
+
+ # The command to handle text that is inserted prior to a newline
+ attr_accessor :insert_cmd
+
+ # An array of commands prepended to compiled code
+ attr_accessor :pre_cmd
+
+ # An array of commands appended to compiled code
+ attr_accessor :post_cmd
+
+ private
+
+ # A buffered text in #compile
+ attr_accessor :content
+
+ def detect_magic_comment(s, enc = nil)
+ re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
+ frozen = nil
+ s.scan(re) do
+ comment = $+
+ comment = $1 if comment[/-\*-\s*([^\s].*?)\s*-\*-$/]
+ case comment
+ when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
+ enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
+ when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
+ frozen = $1
+ end
+ end
+ return enc, frozen
+ end
+
+ def warn_invalid_trim_mode(mode, uplevel:)
+ warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
+ end
+end
diff --git a/lib/erb/def_method.rb b/lib/erb/def_method.rb
new file mode 100644
index 0000000000..17f9c0f9fa
--- /dev/null
+++ b/lib/erb/def_method.rb
@@ -0,0 +1,46 @@
+#--
+# ERB::DefMethod
+#
+# Utility module to define eRuby script as instance method.
+#
+# === Example
+#
+# example.rhtml:
+# <% for item in @items %>
+# <b><%= item %></b>
+# <% end %>
+#
+# example.rb:
+# require 'erb'
+# class MyClass
+# extend ERB::DefMethod
+# def_erb_method('render()', 'example.rhtml')
+# def initialize(items)
+# @items = items
+# end
+# end
+# print MyClass.new([10,20,30]).render()
+#
+# result:
+#
+# <b>10</b>
+#
+# <b>20</b>
+#
+# <b>30</b>
+#
+module ERB::DefMethod
+ # define _methodname_ as instance method of current module, using ERB
+ # object or eRuby file
+ def def_erb_method(methodname, erb_or_fname)
+ if erb_or_fname.kind_of? String
+ fname = erb_or_fname
+ erb = ERB.new(File.read(fname))
+ erb.def_method(self, methodname, fname)
+ else
+ erb = erb_or_fname
+ erb.def_method(self, methodname, erb.filename || '(ERB)')
+ end
+ end
+ module_function :def_erb_method
+end
diff --git a/lib/erb/util.rb b/lib/erb/util.rb
new file mode 100644
index 0000000000..0c1e7482a8
--- /dev/null
+++ b/lib/erb/util.rb
@@ -0,0 +1,62 @@
+#--
+# ERB::Escape
+#
+# A subset of ERB::Util. Unlike ERB::Util#html_escape, we expect/hope
+# Rails will not monkey-patch ERB::Escape#html_escape.
+begin
+ # We don't build the C extension for JRuby, TruffleRuby, and WASM
+ if $LOAD_PATH.resolve_feature_path('erb/escape')
+ require 'erb/escape'
+ end
+rescue LoadError # resolve_feature_path raises LoadError on TruffleRuby 22.3.0
+end
+unless defined?(ERB::Escape)
+ module ERB::Escape
+ def html_escape(s)
+ CGI.escapeHTML(s.to_s)
+ end
+ module_function :html_escape
+ end
+end
+
+#--
+# ERB::Util
+#
+# A utility module for conversion routines, often handy in HTML generation.
+module ERB::Util
+ #
+ # A utility method for escaping HTML tag characters in _s_.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts html_escape("is a > 0 & a < 10?")
+ #
+ # _Generates_
+ #
+ # is a &gt; 0 &amp; a &lt; 10?
+ #
+ include ERB::Escape # html_escape
+ module_function :html_escape
+ alias h html_escape
+ module_function :h
+
+ #
+ # A utility method for encoding the String _s_ as a URL.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
+ #
+ # _Generates_
+ #
+ # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
+ #
+ def url_encode(s)
+ CGI.escapeURIComponent(s.to_s)
+ end
+ alias u url_encode
+ module_function :u
+ module_function :url_encode
+end
diff --git a/lib/erb/version.rb b/lib/erb/version.rb
index 0aaa38258f..38e1b76ff4 100644
--- a/lib/erb/version.rb
+++ b/lib/erb/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class ERB
- VERSION = '2.2.3'
+ VERSION = '4.0.2'
private_constant :VERSION
end
diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb
index 4c115cc828..062871ee16 100644
--- a/lib/error_highlight/base.rb
+++ b/lib/error_highlight/base.rb
@@ -59,8 +59,7 @@ module ErrorHighlight
Spotter.new(node, **opts).spot
when RubyVM::AbstractSyntaxTree::Node
- # Just for compatibility
- Spotter.new(node, **opts).spot
+ Spotter.new(obj, **opts).spot
else
raise TypeError, "Exception is expected"
diff --git a/lib/error_highlight/core_ext.rb b/lib/error_highlight/core_ext.rb
index 00d5671648..b69093f74e 100644
--- a/lib/error_highlight/core_ext.rb
+++ b/lib/error_highlight/core_ext.rb
@@ -37,6 +37,11 @@ module ErrorHighlight
end
NameError.prepend(CoreExt)
- TypeError.prepend(CoreExt)
- ArgumentError.prepend(CoreExt)
+
+ if Exception.method_defined?(:detailed_message)
+ # ErrorHighlight is enabled for TypeError and ArgumentError only when Exception#detailed_message is available.
+ # This is because changing ArgumentError#message is highly incompatible.
+ TypeError.prepend(CoreExt)
+ ArgumentError.prepend(CoreExt)
+ end
end
diff --git a/lib/error_highlight/version.rb b/lib/error_highlight/version.rb
index 4279b6d05f..5afe5f06d6 100644
--- a/lib/error_highlight/version.rb
+++ b/lib/error_highlight/version.rb
@@ -1,3 +1,3 @@
module ErrorHighlight
- VERSION = "0.4.0"
+ VERSION = "0.5.1"
end
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index 8ae5266864..b495078f93 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -12,8 +12,8 @@ end
#
# First, what’s elsewhere. \Module \FileUtils:
#
-# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
-# - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
+# - Inherits from {class Object}[rdoc-ref:Object].
+# - Supplements {class File}[rdoc-ref:File]
# (but is not included or extended there).
#
# Here, module \FileUtils provides methods that are useful for:
@@ -36,6 +36,7 @@ end
# - ::ln, ::link: Creates hard links.
# - ::ln_s, ::symlink: Creates symbolic links.
# - ::ln_sf: Creates symbolic links, overwriting if necessary.
+# - ::ln_sr: Creates symbolic links relative to targets
#
# === Deleting
#
@@ -162,8 +163,8 @@ end
# by applying a special pre-process:
#
# - If the target path points to a directory, this method uses methods
-# {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
-# and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
+# {File#chown}[rdoc-ref:File#chown]
+# and {File#chmod}[rdoc-ref:File#chmod]
# in removing directories.
# - The owner of the target directory should be either the current process
# or the super user (root).
@@ -179,7 +180,7 @@ end
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module FileUtils
- VERSION = "1.6.0"
+ VERSION = "1.7.0"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -291,7 +292,7 @@ module FileUtils
#
# With no keyword arguments, creates a directory at each +path+ in +list+
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
# FileUtils.mkdir('tmp4') # => ["tmp4"]
@@ -299,7 +300,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -339,7 +340,7 @@ module FileUtils
# With no keyword arguments, creates a directory at each +path+ in +list+,
# along with any needed ancestor directories,
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -347,7 +348,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -417,7 +418,7 @@ module FileUtils
#
# With no keyword arguments, removes the directory at each +path+ in +list+,
# by calling: <tt>Dir.rmdir(path)</tt>;
- # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
+ # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]:
#
# FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -690,6 +691,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>relative: false</tt> - create links relative to +dest+.
# - <tt>noop: true</tt> - does not create links.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -709,7 +711,10 @@ module FileUtils
#
# Related: FileUtils.ln_sf.
#
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
+ if relative
+ return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
+ end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
@@ -729,6 +734,48 @@ module FileUtils
end
module_function :ln_sf
+ # Like FileUtils.ln_s, but create links relative to +dest+.
+ #
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
+ options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
+ dest = File.path(dest)
+ srcs = Array(src)
+ link = proc do |s, target_dir_p = true|
+ s = File.path(s)
+ if target_dir_p
+ d = File.join(destdirs = dest, File.basename(s))
+ else
+ destdirs = File.dirname(d = dest)
+ end
+ destdirs = fu_split_path(File.realpath(destdirs))
+ if fu_starting_path?(s)
+ srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
+ base = fu_relative_components_from(srcdirs, destdirs)
+ s = File.join(*base)
+ else
+ srcdirs = fu_clean_components(*fu_split_path(s))
+ base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
+ while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
+ srcdirs.shift
+ base.pop
+ end
+ s = File.join(*base, *srcdirs)
+ end
+ fu_output_message "ln -s#{options} #{s} #{d}" if verbose
+ next if noop
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ case srcs.size
+ when 0
+ when 1
+ link[srcs[0], target_directory && File.directory?(dest)]
+ else
+ srcs.each(&link)
+ end
+ end
+ module_function :ln_sr
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
#
# Arguments +src+ and +dest+
@@ -1044,7 +1091,7 @@ module FileUtils
module_function :copy_file
# Copies \IO stream +src+ to \IO stream +dest+ via
- # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
+ # {IO.copy_stream}[rdoc-ref:IO.copy_stream].
#
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
@@ -1555,14 +1602,14 @@ module FileUtils
# Keyword arguments:
#
# - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>mode: <i>permissions</i></tt> - changes the permissions.
- # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # using {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not copy entries; returns +nil+.
# - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>preserve: true</tt> - preserve timestamps
- # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
+ # using {File.utime}[rdoc-ref:File.utime].
# - <tt>verbose: true</tt> - prints an equivalent command:
#
# FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
@@ -1699,9 +1746,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # {File.chmod}[rdoc-ref:File.chmod].
# - Modifies each entry that is a symbolic link using
- # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
+ # {File.lchmod}[rdoc-ref:File.lchmod].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -1801,9 +1848,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # {File.chown}[rdoc-ref:File.chown].
# - Modifies each entry that is a symbolic link using
- # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
+ # {File.lchown}[rdoc-ref:File.lchown].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -2328,13 +2375,21 @@ module FileUtils
def postorder_traverse
if directory?
- entries().each do |ent|
+ begin
+ children = entries()
+ rescue Errno::EACCES
+ # Failed to get the list of children.
+ # Assuming there is no children, try to process the parent directory.
+ yield self
+ return
+ end
+
+ children.each do |ent|
ent.postorder_traverse do |e|
yield e
end
end
end
- ensure
yield self
end
@@ -2428,15 +2483,15 @@ module FileUtils
end
private_module_function :fu_each_src_dest
- def fu_each_src_dest0(src, dest) #:nodoc:
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
if tmp = Array.try_convert(src)
tmp.each do |s|
s = File.path(s)
- yield s, File.join(dest, File.basename(s))
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
end
else
src = File.path(src)
- if File.directory?(dest)
+ if target_directory and File.directory?(dest)
yield src, File.join(dest, File.basename(src))
else
yield src, File.path(dest)
@@ -2460,6 +2515,56 @@ module FileUtils
end
private_module_function :fu_output_message
+ def fu_split_path(path)
+ path = File.path(path)
+ list = []
+ until (parent, base = File.split(path); parent == path or parent == ".")
+ list << base
+ path = parent
+ end
+ list << path
+ list.reverse!
+ end
+ private_module_function :fu_split_path
+
+ def fu_relative_components_from(target, base) #:nodoc:
+ i = 0
+ while target[i]&.== base[i]
+ i += 1
+ end
+ Array.new(base.size-i, '..').concat(target[i..-1])
+ end
+ private_module_function :fu_relative_components_from
+
+ def fu_clean_components(*comp)
+ comp.shift while comp.first == "."
+ return comp if comp.empty?
+ clean = [comp.shift]
+ path = File.join(*clean, "") # ending with File::SEPARATOR
+ while c = comp.shift
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
+ clean.pop
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ else
+ clean << c
+ path << c << "/"
+ end
+ end
+ clean
+ end
+ private_module_function :fu_clean_components
+
+ if fu_windows?
+ def fu_starting_path?(path)
+ path&.start_with?(%r(\w:|/))
+ end
+ else
+ def fu_starting_path?(path)
+ path&.start_with?("/")
+ end
+ end
+ private_module_function :fu_starting_path?
+
# This hash table holds command options.
OPT_TABLE = {} #:nodoc: internal use only
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
index c9c4128f9f..71b4e6adad 100644
--- a/lib/forwardable.rb
+++ b/lib/forwardable.rb
@@ -112,8 +112,10 @@ module Forwardable
require 'forwardable/impl'
# Version of +forwardable.rb+
- VERSION = "1.3.2"
+ VERSION = "1.3.3"
+ VERSION.freeze
FORWARDABLE_VERSION = VERSION
+ FORWARDABLE_VERSION.freeze
@debug = nil
class << self
diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb
index 0810ed7a57..5ae0e1497c 100644
--- a/lib/getoptlong.rb
+++ b/lib/getoptlong.rb
@@ -368,7 +368,7 @@
#
class GetoptLong
# Version.
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
#
# Orderings.
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb
index 459b8478a0..7a5cf94830 100644
--- a/lib/ipaddr.rb
+++ b/lib/ipaddr.rb
@@ -40,14 +40,14 @@ require 'socket'
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
class IPAddr
- VERSION = "1.2.4"
+ VERSION = "1.2.5"
# 32 bit mask for IPv4
IN4MASK = 0xffffffff
# 128 bit mask for IPv6
IN6MASK = 0xffffffffffffffffffffffffffffffff
# Format string for IPv6
- IN6FORMAT = (["%.4x"] * 8).join(':')
+ IN6FORMAT = (["%.4x"] * 8).join(':').freeze
# Regexp _internally_ used for parsing IPv4 address.
RE_IPV4ADDRLIKE = %r{
@@ -736,7 +736,7 @@ end
unless Socket.const_defined? :AF_INET6
class Socket < BasicSocket
# IPv6 protocol family
- AF_INET6 = Object.new
+ AF_INET6 = Object.new.freeze
end
class << IPSocket
diff --git a/lib/irb.rb b/lib/irb.rb
index 9a3a491641..2db99bcd43 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -53,6 +53,52 @@ require_relative "irb/easter-egg"
#
# :include: ./irb/lc/help-message
#
+# == Commands
+#
+# The following commands are available on IRB.
+#
+# * cwws
+# * Show the current workspace.
+# * cb, cws, chws
+# * Change the current workspace to an object.
+# * bindings, workspaces
+# * Show workspaces.
+# * pushb, pushws
+# * Push an object to the workspace stack.
+# * popb, popws
+# * Pop a workspace from the workspace stack.
+# * load
+# * Load a Ruby file.
+# * require
+# * Require a Ruby file.
+# * source
+# * Loads a given file in the current session.
+# * irb
+# * Start a child IRB.
+# * jobs
+# * List of current sessions.
+# * fg
+# * Switches to the session of the given number.
+# * kill
+# * Kills the session with the given number.
+# * help
+# * Enter the mode to look up RI documents.
+# * irb_info
+# * Show information about IRB.
+# * ls
+# * Show methods, constants, and variables.
+# -g [query] or -G [query] allows you to filter out the output.
+# * measure
+# * measure enables the mode to measure processing time. measure :off disables it.
+# * $, show_source
+# * Show the source code of a given method or constant.
+# * @, whereami
+# * Show the source code around binding.irb again.
+# * debug
+# * Start the debugger of debug.gem.
+# * break, delete, next, step, continue, finish, backtrace, info, catch
+# * Start the debugger of debug.gem and run the command on it.
+#
# == Configuration
#
# IRB reads a personal initialization file when it's invoked.
@@ -93,9 +139,9 @@ require_relative "irb/easter-egg"
#
# === Autocompletion
#
-# To enable autocompletion for irb, add the following to your +.irbrc+:
+# To disable autocompletion for irb, add the following to your +.irbrc+:
#
-# require 'irb/completion'
+# IRB.conf[:USE_AUTOCOMPLETE] = false
#
# === History
#
@@ -389,11 +435,7 @@ module IRB
#
# Will raise an Abort exception, or the given +exception+.
def IRB.irb_abort(irb, exception = Abort)
- if defined? Thread
- irb.context.thread.raise exception, "abort then interrupt!"
- else
- raise exception, "abort then interrupt!"
- end
+ irb.context.thread.raise exception, "abort then interrupt!"
end
class Irb
@@ -434,6 +476,17 @@ module IRB
@scanner = RubyLex.new
end
+ # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
+ def debug_break
+ # it means the debug command is executed
+ if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ # after leaving this initial breakpoint, revert the capture_frames patch
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb)
+ # and remove the redundant method
+ DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb)
+ end
+ end
+
def run(conf = IRB.conf)
conf[:IRB_RC].call(context) if conf[:IRB_RC]
conf[:MAIN_CONTEXT] = context
@@ -510,13 +563,15 @@ module IRB
@scanner.set_auto_indent(@context) if @context.auto_indent_mode
- @scanner.each_top_level_statement do |line, line_no|
+ @scanner.each_top_level_statement(@context) do |line, line_no|
signal_status(:IN_EVAL) do
begin
line.untaint if RUBY_VERSION < '2.7'
if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
IRB.set_measure_callback
end
+ # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1`
+ is_assignment = assignment_expression?(line)
if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
result = nil
last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) }
@@ -533,7 +588,7 @@ module IRB
@context.evaluate(line, line_no, exception: exc)
end
if @context.echo?
- if assignment_expression?(line)
+ if is_assignment
if @context.echo_on_assignment?
output_value(@context.echo_on_assignment? == :truncate)
end
@@ -596,11 +651,7 @@ module IRB
if exc.backtrace
order = nil
- if '2.5.0' == RUBY_VERSION
- # Exception#full_message doesn't have keyword arguments.
- message = exc.full_message # the same of (highlight: true, order: bottom)
- order = :bottom
- elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
+ if RUBY_VERSION < '3.0.0'
if STDOUT.tty?
message = exc.full_message(order: :bottom)
order = :bottom
@@ -831,9 +882,12 @@ module IRB
# array of parsed expressions. The first element of each expression is the
# expression's type.
verbose, $VERBOSE = $VERBOSE, nil
- result = ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0))
+ code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
+ # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
+ node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
+ ASSIGNMENT_NODE_TYPES.include?(node_type)
+ ensure
$VERBOSE = verbose
- result
end
ATTR_TTY = "\e[%sm"
@@ -923,12 +977,13 @@ class Binding
#
#
# See IRB@IRB+Usage for more information.
- def irb
+ def irb(show_code: true)
IRB.setup(source_location[0], argv: [])
workspace = IRB::WorkSpace.new(self)
- STDOUT.print(workspace.code_around_binding)
+ STDOUT.print(workspace.code_around_binding) if show_code
binding_irb = IRB::Irb.new(workspace)
binding_irb.context.irb_path = File.expand_path(source_location[0])
binding_irb.run(IRB.conf)
+ binding_irb.debug_break
end
end
diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb
new file mode 100644
index 0000000000..f632894618
--- /dev/null
+++ b/lib/irb/cmd/backtrace.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Backtrace < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["backtrace", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb
new file mode 100644
index 0000000000..df259a90ca
--- /dev/null
+++ b/lib/irb/cmd/break.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Break < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(args = nil)
+ super(pre_cmds: "break #{args}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb
new file mode 100644
index 0000000000..40b62c7533
--- /dev/null
+++ b/lib/irb/cmd/catch.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Catch < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["catch", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb
index b28c090686..7c84ba0a4b 100644
--- a/lib/irb/cmd/chws.rb
+++ b/lib/irb/cmd/chws.rb
@@ -19,12 +19,18 @@ module IRB
module ExtendCommand
class CurrentWorkingWorkspace < Nop
+ category "IRB"
+ description "Show the current workspace."
+
def execute(*obj)
irb_context.main
end
end
class ChangeWorkspace < Nop
+ category "IRB"
+ description "Change the current workspace to an object."
+
def execute(*obj)
irb_context.change_workspace(*obj)
irb_context.main
diff --git a/lib/irb/cmd/continue.rb b/lib/irb/cmd/continue.rb
new file mode 100644
index 0000000000..9136177eef
--- /dev/null
+++ b/lib/irb/cmd/continue.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Continue < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["continue", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb
new file mode 100644
index 0000000000..7d39b9fa27
--- /dev/null
+++ b/lib/irb/cmd/debug.rb
@@ -0,0 +1,136 @@
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Debug < Nop
+ category "Debugging"
+ description "Start the debugger of debug.gem."
+
+ BINDING_IRB_FRAME_REGEXPS = [
+ '<internal:prelude>',
+ binding.method(:irb).source_location.first,
+ ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
+ IRB_DIR = File.expand_path('..', __dir__)
+
+ def execute(pre_cmds: nil, do_cmds: nil)
+ unless binding_irb?
+ puts "`debug` command is only available when IRB is started with binding.irb"
+ return
+ end
+
+ unless setup_debugger
+ puts <<~MSG
+ You need to install the debug gem before using this command.
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
+ MSG
+ return
+ end
+
+ options = { oneshot: true, hook_call: false }
+ if pre_cmds || do_cmds
+ options[:command] = ['irb', pre_cmds, do_cmds]
+ end
+ if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
+ options[:skip_src] = true
+ end
+
+ # To make debugger commands like `next` or `continue` work without asking
+ # the user to quit IRB after that, we need to exit IRB first and then hit
+ # a TracePoint on #debug_break.
+ file, lineno = IRB::Irb.instance_method(:debug_break).source_location
+ DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
+ # exit current Irb#run call
+ throw :IRB_EXIT
+ end
+
+ private
+
+ def binding_irb?
+ caller.any? do |frame|
+ BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
+ frame.match?(regexp)
+ end
+ end
+ end
+
+ module SkipPathHelperForIRB
+ def skip_internal_path?(path)
+ # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
+ super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
+ end
+ end
+
+ def setup_debugger
+ unless defined?(DEBUGGER__::SESSION)
+ begin
+ require "debug/session"
+ rescue LoadError # debug.gem is not written in Gemfile
+ return false unless load_bundled_debug_gem
+ end
+ DEBUGGER__.start(nonstop: true)
+ end
+
+ unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
+
+ def DEBUGGER__.capture_frames(*args)
+ frames = capture_frames_without_irb(*args)
+ frames.reject! do |frame|
+ frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
+ end
+ frames
+ end
+
+ DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
+ end
+
+ true
+ end
+
+ # This is used when debug.gem is not written in Gemfile. Even if it's not
+ # installed by `bundle install`, debug.gem is installed by default because
+ # it's a bundled gem. This method tries to activate and load that.
+ def load_bundled_debug_gem
+ # Discover latest debug.gem under GEM_PATH
+ debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
+ File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
+ end.sort_by do |path|
+ Gem::Version.new(File.basename(path).delete_prefix('debug-'))
+ end.last
+ return false unless debug_gem
+
+ # Discover debug/debug.so under extensions for Ruby 3.2+
+ ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
+ ext_path = Gem.paths.path.flat_map do |path|
+ Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
+ end.first
+
+ # Attempt to forcibly load the bundled gem
+ if ext_path
+ $LOAD_PATH << ext_path.delete_suffix(ext_name)
+ end
+ $LOAD_PATH << "#{debug_gem}/lib"
+ begin
+ require "debug/session"
+ puts "Loaded #{File.basename(debug_gem)}"
+ true
+ rescue LoadError
+ false
+ end
+ end
+ end
+
+ class DebugCommand < Debug
+ def self.category
+ "Debugging"
+ end
+
+ def self.description
+ command_name = self.name.split("::").last.downcase
+ "Start the debugger of debug.gem and run its `#{command_name}` command."
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/delete.rb b/lib/irb/cmd/delete.rb
new file mode 100644
index 0000000000..aeb26d2572
--- /dev/null
+++ b/lib/irb/cmd/delete.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Delete < DebugCommand
+ def execute(*args)
+ super(pre_cmds: ["delete", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb
new file mode 100644
index 0000000000..0103891cf4
--- /dev/null
+++ b/lib/irb/cmd/edit.rb
@@ -0,0 +1,61 @@
+require 'shellwords'
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Edit < Nop
+ category "Misc"
+ description 'Open a file with the editor command defined with `ENV["EDITOR"]`.'
+
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.nil? || args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+ end
+
+ def execute(*args)
+ path = args.first
+
+ if path.nil? && (irb_path = @irb_context.irb_path)
+ path = irb_path
+ end
+
+ if !File.exist?(path)
+ require_relative "show_source"
+
+ source =
+ begin
+ ShowSource.find_source(path, @irb_context)
+ rescue NameError
+ # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
+ # in this case, we should just ignore the error
+ end
+
+ if source && File.exist?(source.file)
+ path = source.file
+ else
+ puts "Can not find file: #{path}"
+ return
+ end
+ end
+
+ if editor = ENV['EDITOR']
+ puts "command: '#{editor}'"
+ puts " path: #{path}"
+ system(*Shellwords.split(editor), path)
+ else
+ puts "Can not find editor setting: ENV['EDITOR']"
+ end
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/finish.rb b/lib/irb/cmd/finish.rb
new file mode 100644
index 0000000000..29f100feb5
--- /dev/null
+++ b/lib/irb/cmd/finish.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Finish < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["finish", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb
index 0497c57457..2a135cdb14 100644
--- a/lib/irb/cmd/help.rb
+++ b/lib/irb/cmd/help.rb
@@ -16,6 +16,20 @@ module IRB
module ExtendCommand
class Help < Nop
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+ end
+
+ category "Context"
+ description "Enter the mode to look up RI documents."
+
def execute(*names)
require 'rdoc/ri/driver'
opts = RDoc::RI::Driver.process_args([])
diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb
index 37ecd762ff..2c0a32b34f 100644
--- a/lib/irb/cmd/info.rb
+++ b/lib/irb/cmd/info.rb
@@ -1,31 +1,18 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
-require_relative "nop"
+require_relative "debug"
module IRB
# :stopdoc:
module ExtendCommand
- class Info < Nop
- def execute
- Class.new {
- def inspect
- str = "Ruby version: #{RUBY_VERSION}\n"
- str += "IRB version: #{IRB.version}\n"
- str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
- str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
- str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
- str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
- str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
- str += "Code page: #{codepage}\n"
- end
- str
- end
- alias_method :to_s, :inspect
- }.new
+ class Info < DebugCommand
+ def self.transform_args(args)
+ args&.dump
+ end
+
+ def execute(*args)
+ super(pre_cmds: ["info", *args].join(" "))
end
end
end
diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/cmd/irb_info.rb
new file mode 100644
index 0000000000..da11e8d40b
--- /dev/null
+++ b/lib/irb/cmd/irb_info.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: false
+
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class IrbInfo < Nop
+ category "IRB"
+ description "Show information about IRB."
+
+ def execute
+ Class.new {
+ def inspect
+ str = "Ruby version: #{RUBY_VERSION}\n"
+ str += "IRB version: #{IRB.version}\n"
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
+ str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
+ str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
+ str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
+ str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
+ codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
+ str += "Code page: #{codepage}\n"
+ end
+ str
+ end
+ alias_method :to_s, :inspect
+ }.new
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/load.rb b/lib/irb/cmd/load.rb
index 2c5c01e89c..2897bbd975 100644
--- a/lib/irb/cmd/load.rb
+++ b/lib/irb/cmd/load.rb
@@ -17,18 +17,29 @@ module IRB
# :stopdoc:
module ExtendCommand
- class Load < Nop
+ class LoaderCommand < Nop
include IrbLoader
- def execute(file_name, priv = nil)
- return irb_load(file_name, priv)
+ def raise_cmd_argument_error
+ raise CommandArgumentError.new("Please specify the file name.")
end
end
- class Require < Nop
- include IrbLoader
+ class Load < LoaderCommand
+ category "IRB"
+ description "Load a Ruby file."
+
+ def execute(file_name = nil, priv = nil)
+ raise_cmd_argument_error unless file_name
+ irb_load(file_name, priv)
+ end
+ end
- def execute(file_name)
+ class Require < LoaderCommand
+ category "IRB"
+ description "Require a Ruby file."
+ def execute(file_name = nil)
+ raise_cmd_argument_error unless file_name
rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
return false if $".find{|f| f =~ rex}
@@ -56,13 +67,16 @@ module IRB
end
end
- class Source < Nop
- include IrbLoader
- def execute(file_name)
+ class Source < LoaderCommand
+ category "IRB"
+ description "Loads a given file in the current session."
+
+ def execute(file_name = nil)
+ raise_cmd_argument_error unless file_name
+
source_file(file_name)
end
end
end
-
# :startdoc:
end
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
index f4a7348bd1..b65fae2bf1 100644
--- a/lib/irb/cmd/ls.rb
+++ b/lib/irb/cmd/ls.rb
@@ -9,6 +9,18 @@ module IRB
module ExtendCommand
class Ls < Nop
+ category "Context"
+ description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."
+
+ def self.transform_args(args)
+ if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
+ args = match[:args]
+ "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
+ else
+ args
+ end
+ end
+
def execute(*arg, grep: nil)
o = Output.new(grep: grep)
@@ -21,6 +33,7 @@ module IRB
o.dump("instance variables", obj.instance_variables)
o.dump("class variables", klass.class_variables)
o.dump("locals", locals)
+ nil
end
def dump_methods(o, klass, obj)
diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb
index a97baee9f1..9122e2dac9 100644
--- a/lib/irb/cmd/measure.rb
+++ b/lib/irb/cmd/measure.rb
@@ -5,6 +5,9 @@ module IRB
module ExtendCommand
class Measure < Nop
+ category "Misc"
+ description "`measure` enables the mode to measure processing time. `measure :off` disables it."
+
def initialize(*args)
super(*args)
end
diff --git a/lib/irb/cmd/next.rb b/lib/irb/cmd/next.rb
new file mode 100644
index 0000000000..d29c82e7fc
--- /dev/null
+++ b/lib/irb/cmd/next.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Next < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["next", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index 881a736722..c616c054a8 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -13,17 +13,41 @@ module IRB
# :stopdoc:
module ExtendCommand
+ class CommandArgumentError < StandardError; end
+
class Nop
+ class << self
+ def category(category = nil)
+ @category = category if category
+ @category
+ end
+
+ def description(description = nil)
+ @description = description if description
+ @description
+ end
+
+ private
+
+ def string_literal?(args)
+ sexp = Ripper.sexp(args)
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
+ end
+ end
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
def self.execute(conf, *opts, **kwargs, &block)
command = new(conf)
command.execute(*opts, **kwargs, &block)
+ rescue CommandArgumentError => e
+ puts e.message
end
else
def self.execute(conf, *opts, &block)
command = new(conf)
command.execute(*opts, &block)
+ rescue CommandArgumentError => e
+ puts e.message
end
end
diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb
index 791d8f8dbb..41d2e705f1 100644
--- a/lib/irb/cmd/pushws.rb
+++ b/lib/irb/cmd/pushws.rb
@@ -18,12 +18,18 @@ module IRB
module ExtendCommand
class Workspaces < Nop
+ category "IRB"
+ description "Show workspaces."
+
def execute(*obj)
irb_context.workspaces.collect{|ws| ws.main}
end
end
class PushWorkspace < Workspaces
+ category "IRB"
+ description "Push an object to the workspace stack."
+
def execute(*obj)
irb_context.push_workspace(*obj)
super
@@ -31,6 +37,9 @@ module IRB
end
class PopWorkspace < Workspaces
+ category "IRB"
+ description "Pop a workspace from the workspace stack."
+
def execute(*obj)
irb_context.pop_workspace(*obj)
super
diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb
new file mode 100644
index 0000000000..acced27d48
--- /dev/null
+++ b/lib/irb/cmd/show_cmds.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "stringio"
+require_relative "nop"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class ShowCmds < Nop
+ category "IRB"
+ description "List all available commands and their description."
+
+ def execute(*args)
+ commands_info = IRB::ExtendCommandBundle.all_commands_info
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
+
+ output = StringIO.new
+
+ commands_grouped_by_categories.each do |category, cmds|
+ output.puts Color.colorize(category, [:BOLD])
+
+ cmds.each do |cmd|
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
+ end
+
+ output.puts
+ end
+
+ puts output.string
+
+ nil
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
index f8a17822df..ea700be4bf 100644
--- a/lib/irb/cmd/show_source.rb
+++ b/lib/irb/cmd/show_source.rb
@@ -9,12 +9,71 @@ module IRB
module ExtendCommand
class ShowSource < Nop
+ category "Context"
+ description "Show the source code of a given method or constant."
+
+ class << self
+ def transform_args(args)
+ # Return a string literal as is for backward compatibility
+ if args.empty? || string_literal?(args)
+ args
+ else # Otherwise, consider the input as a String for convenience
+ args.strip.dump
+ end
+ end
+
+ def find_source(str, irb_context)
+ case str
+ when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
+ eval(str, irb_context.workspace.binding) # trigger autoload
+ base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
+ file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
+ owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
+ file, line = owner.instance_method(method).source_location
+ end
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
+ receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ file, line = receiver.method(method).source_location if receiver.respond_to?(method)
+ end
+ if file && line
+ Source.new(file: file, first_line: line, last_line: find_end(file, line))
+ end
+ end
+
+ private
+
+ def find_end(file, first_line)
+ return first_line unless File.exist?(file)
+ lex = RubyLex.new
+ lines = File.read(file).lines[(first_line - 1)..-1]
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
+ prev_tokens = []
+
+ # chunk with line number
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
+ code = lines[0..lnum].join
+ prev_tokens.concat chunk
+ continue = lex.process_continue(prev_tokens)
+ code_block_open = lex.check_code_block(code, prev_tokens)
+ if !continue && !code_block_open
+ return first_line + lnum
+ end
+ end
+ first_line
+ end
+ end
+
def execute(str = nil)
unless str.is_a?(String)
puts "Error: Expected a string but got #{str.inspect}"
return
end
- source = find_source(str)
+
+ source = self.class.find_source(str, @irb_context)
if source && File.exist?(source.file)
show_source(source)
else
@@ -35,48 +94,6 @@ module IRB
puts
end
- def find_source(str)
- case str
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
- eval(str, irb_context.workspace.binding) # trigger autoload
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
- file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
- file, line = owner.instance_method(method).source_location
- end
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
- end
- if file && line
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
- end
- end
-
- def find_end(file, first_line)
- return first_line unless File.exist?(file)
- lex = RubyLex.new
- lines = File.read(file).lines[(first_line - 1)..-1]
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
- prev_tokens = []
-
- # chunk with line number
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
- code = lines[0..lnum].join
- prev_tokens.concat chunk
- continue = lex.process_continue(prev_tokens)
- code_block_open = lex.check_code_block(code, prev_tokens)
- if !continue && !code_block_open
- return first_line + lnum
- end
- end
- first_line
- end
-
def bold(str)
Color.colorize(str, [:BOLD])
end
diff --git a/lib/irb/cmd/step.rb b/lib/irb/cmd/step.rb
new file mode 100644
index 0000000000..2bc74a9d79
--- /dev/null
+++ b/lib/irb/cmd/step.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module ExtendCommand
+ class Step < DebugCommand
+ def execute(*args)
+ super(do_cmds: ["step", *args].join(" "))
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb
index b322aadc53..699b35fcb4 100644
--- a/lib/irb/cmd/subirb.rb
+++ b/lib/irb/cmd/subirb.rb
@@ -10,31 +10,57 @@
#
require_relative "nop"
-require_relative "../ext/multi-irb"
module IRB
# :stopdoc:
module ExtendCommand
- class IrbCommand < Nop
+ class MultiIRBCommand < Nop
+ def initialize(conf)
+ super
+ extend_irb_context
+ end
+
+ private
+
+ def extend_irb_context
+ # this extension patches IRB context like IRB.CurrentContext
+ require_relative "../ext/multi-irb"
+ end
+ end
+
+ class IrbCommand < MultiIRBCommand
+ category "IRB"
+ description "Start a child IRB."
+
def execute(*obj)
IRB.irb(nil, *obj)
end
end
- class Jobs < Nop
+ class Jobs < MultiIRBCommand
+ category "IRB"
+ description "List of current sessions."
+
def execute
IRB.JobManager
end
end
- class Foreground < Nop
- def execute(key)
+ class Foreground < MultiIRBCommand
+ category "IRB"
+ description "Switches to the session of the given number."
+
+ def execute(key = nil)
+ raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
IRB.JobManager.switch(key)
end
end
- class Kill < Nop
+ class Kill < MultiIRBCommand
+ category "IRB"
+ description "Kills the session with the given number."
+
def execute(*keys)
IRB.JobManager.kill(*keys)
end
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb
index b8c7e047fa..8f56ba073d 100644
--- a/lib/irb/cmd/whereami.rb
+++ b/lib/irb/cmd/whereami.rb
@@ -7,6 +7,9 @@ module IRB
module ExtendCommand
class Whereami < Nop
+ category "Context"
+ description "Show the source code around binding.irb again."
+
def execute(*)
code = irb_context.workspace.code_around_binding
if code
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index 8307af25a9..6378e14856 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -123,15 +123,21 @@ module IRB # :nodoc:
# If `complete` is false (code is incomplete), this does not warn compile_error.
# This option is needed to avoid warning a user when the compile_error is happening
# because the input is not wrong but just incomplete.
- def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?)
+ def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
return code unless colorable
symbol_state = SymbolState.new
colored = +''
- length = 0
- end_seen = false
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
+ code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
+
+ scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr|
+ # handle uncolorable code
+ if token.nil?
+ colored << Reline::Unicode.escape_for_print(str)
+ next
+ end
- scan(code, allow_last_error: !complete) do |token, str, expr|
# IRB::ColorPrinter skips colorizing fragments with any invalid token
if ignore_error && ERROR_TOKENS.include?(token)
return Reline::Unicode.escape_for_print(code)
@@ -147,15 +153,12 @@ module IRB # :nodoc:
colored << line
end
end
- length += str.bytesize
- end_seen = true if token == :on___end__
end
- # give up colorizing incomplete Ripper tokens
- unless end_seen or length == code.bytesize
- return Reline::Unicode.escape_for_print(code)
+ if lvars_code
+ raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
+ colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
end
-
colored
end
@@ -170,33 +173,42 @@ module IRB # :nodoc:
end
def scan(code, allow_last_error:)
- pos = [1, 0]
-
verbose, $VERBOSE = $VERBOSE, nil
RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no|
lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no)
- if lexer.respond_to?(:scan) # Ruby 2.7+
- lexer.scan.each do |elem|
- str = elem.tok
- next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
- next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0
+ byte_pos = 0
+ line_positions = [0]
+ inner_code.lines.each do |line|
+ line_positions << line_positions.last + line.bytesize
+ end
- str.each_line do |line|
- if line.end_with?("\n")
- pos[0] += 1
- pos[1] = 0
- else
- pos[1] += line.bytesize
- end
- end
+ on_scan = proc do |elem|
+ start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1]
+ # yield uncolorable code
+ if byte_pos < start_pos
+ yield(nil, inner_code.byteslice(byte_pos...start_pos), nil)
+ end
+
+ if byte_pos <= start_pos
+ str = elem.tok
yield(elem.event, str, elem.state)
+ byte_pos = start_pos + str.bytesize
+ end
+ end
+
+ if lexer.respond_to?(:scan) # Ruby 2.7+
+ lexer.scan.each do |elem|
+ next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
+ on_scan.call(elem)
end
else
- lexer.parse.each do |elem|
- yield(elem.event, elem.tok, elem.state)
+ lexer.parse.sort_by(&:pos).each do |elem|
+ on_scan.call(elem)
end
end
+ # yield uncolorable DATA section
+ yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize
end
ensure
$VERBOSE = verbose
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 9121174a50..34640e17f9 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -11,7 +11,29 @@ require_relative 'ruby-lex'
module IRB
module InputCompletor # :nodoc:
+ using Module.new {
+ refine ::Binding do
+ def eval_methods
+ ::Kernel.instance_method(:methods).bind(eval("self")).call
+ end
+
+ def eval_private_methods
+ ::Kernel.instance_method(:private_methods).bind(eval("self")).call
+ end
+ def eval_instance_variables
+ ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
+ end
+
+ def eval_global_variables
+ ::Kernel.instance_method(:global_variables).bind(eval("self")).call
+ end
+
+ def eval_class_constants
+ ::Module.instance_method(:constants).bind(eval("self.class")).call
+ end
+ end
+ }
# Set of reserved words used by Ruby, you should not use these for
# constants or variables
@@ -42,25 +64,34 @@ module IRB
if File.respond_to?(:absolute_path?)
File.absolute_path?(p)
else
- if File.absolute_path(p) == p
- true
- else
- false
- end
+ File.absolute_path(p) == p
end
end
+ GEM_PATHS =
+ if defined?(Gem::Specification)
+ Gem::Specification.latest_specs(true).map { |s|
+ s.require_paths.map { |p|
+ if absolute_path?(p)
+ p
+ else
+ File.join(s.full_gem_path, p)
+ end
+ }
+ }.flatten
+ else
+ []
+ end.freeze
+
def self.retrieve_gem_and_system_load_path
- gem_paths = Gem::Specification.latest_specs(true).map { |s|
- s.require_paths.map { |p|
- if absolute_path?(p)
- p
- else
- File.join(s.full_gem_path, p)
- end
- }
- }.flatten if defined?(Gem::Specification)
- (gem_paths.to_a | $LOAD_PATH).sort
+ candidates = (GEM_PATHS | $LOAD_PATH)
+ candidates.map do |p|
+ if p.respond_to?(:to_path)
+ p.to_path
+ else
+ String(p) rescue nil
+ end
+ end.compact.sort
end
def self.retrieve_files_to_require_from_load_path
@@ -142,10 +173,10 @@ module IRB
receiver = $1
message = $3
- candidates = String.instance_methods.collect{|m| m.to_s}
if doc_namespace
"String.#{message}"
else
+ candidates = String.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -154,10 +185,10 @@ module IRB
receiver = $1
message = $2
- candidates = Regexp.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Regexp.#{message}"
else
+ candidates = Regexp.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -166,10 +197,10 @@ module IRB
receiver = $1
message = $2
- candidates = Array.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Array.#{message}"
else
+ candidates = Array.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates)
end
@@ -178,29 +209,33 @@ module IRB
receiver = $1
message = $2
- proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
- hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
if doc_namespace
["Proc.#{message}", "Hash.#{message}"]
else
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
+ hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, proc_candidates | hash_candidates)
end
when /^(:[^:.]*)$/
# Symbol
- return nil if doc_namespace
- sym = $1
- candidates = Symbol.all_symbols.collect do |s|
- ":" + s.id2name.encode(Encoding.default_external)
- rescue EncodingError
- # ignore
+ if doc_namespace
+ nil
+ else
+ sym = $1
+ candidates = Symbol.all_symbols.collect do |s|
+ ":" + s.id2name.encode(Encoding.default_external)
+ rescue EncodingError
+ # ignore
+ end
+ candidates.grep(/^#{Regexp.quote(sym)}/)
end
- candidates.grep(/^#{Regexp.quote(sym)}/)
-
when /^::([A-Z][^:\.\(\)]*)$/
# Absolute Constant or class methods
receiver = $1
+
candidates = Object.constants.collect{|m| m.to_s}
+
if doc_namespace
candidates.find { |i| i == receiver }
else
@@ -211,16 +246,18 @@ module IRB
# Constant or class methods
receiver = $1
message = $2
- begin
- candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
- candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
- rescue Exception
- candidates = []
- end
+
if doc_namespace
"#{receiver}::#{message}"
else
- select_message(receiver, message, candidates, "::")
+ begin
+ candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
+ candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
+ rescue Exception
+ candidates = []
+ end
+
+ select_message(receiver, message, candidates.sort, "::")
end
when /^(:[^:.]+)(\.|::)([^.]*)$/
@@ -229,10 +266,10 @@ module IRB
sep = $2
message = $3
- candidates = Symbol.instance_methods.collect{|m| m.to_s}
if doc_namespace
"Symbol.#{message}"
else
+ candidates = Symbol.instance_methods.collect{|m| m.to_s}
select_message(receiver, message, candidates, sep)
end
@@ -244,6 +281,7 @@ module IRB
begin
instance = eval(receiver, bind)
+
if doc_namespace
"#{instance.class.name}.#{message}"
else
@@ -254,7 +292,7 @@ module IRB
if doc_namespace
nil
else
- candidates = []
+ []
end
end
@@ -276,7 +314,7 @@ module IRB
if doc_namespace
nil
else
- candidates = []
+ []
end
end
@@ -284,6 +322,7 @@ module IRB
# global var
gvar = $1
all_gvars = global_variables.collect{|m| m.to_s}
+
if doc_namespace
all_gvars.find{ |i| i == gvar }
else
@@ -296,10 +335,10 @@ module IRB
sep = $2
message = $3
- gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
- lv = eval("local_variables", bind).collect{|m| m.to_s}
- iv = eval("instance_variables", bind).collect{|m| m.to_s}
- cv = eval("self.class.constants", bind).collect{|m| m.to_s}
+ gv = bind.eval_global_variables.collect{|m| m.to_s}.push("true", "false", "nil")
+ lv = bind.local_variables.collect{|m| m.to_s}
+ iv = bind.eval_instance_variables.collect{|m| m.to_s}
+ cv = bind.eval_class_constants.collect{|m| m.to_s}
if (gv | lv | iv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
# foo.func and foo is var. OR
@@ -322,11 +361,13 @@ module IRB
to_ignore = ignored_modules
ObjectSpace.each_object(Module){|m|
next if (to_ignore.include?(m) rescue true)
+ next unless m.respond_to?(:instance_methods) # JRuby has modules that represent java packages. They don't include many common ruby methods
candidates.concat m.instance_methods(false).collect{|x| x.to_s}
}
candidates.sort!
candidates.uniq!
end
+
if doc_namespace
rec_class = rec.is_a?(Module) ? rec : rec.class
"#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
@@ -341,27 +382,28 @@ module IRB
message = $1
candidates = String.instance_methods(true).collect{|m| m.to_s}
+
if doc_namespace
"String.#{candidates.find{ |i| i == message }}"
else
- select_message(receiver, message, candidates)
+ select_message(receiver, message, candidates.sort)
end
else
if doc_namespace
- vars = eval("local_variables | instance_variables", bind).collect{|m| m.to_s}
+ vars = (bind.local_variables | bind.eval_instance_variables).collect{|m| m.to_s}
perfect_match_var = vars.find{|m| m.to_s == input}
if perfect_match_var
eval("#{perfect_match_var}.class.name", bind)
else
- candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
+ candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
candidates.find{ |i| i == input }
end
else
- candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
+ candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
- candidates.grep(/^#{Regexp.quote(input)}/)
+ candidates.grep(/^#{Regexp.quote(input)}/).sort
end
end
end
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index e6c993d423..91fbb2fcf1 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -22,7 +22,7 @@ module IRB
#
# The optional +input_method+ argument:
#
- # +nil+:: uses stdin or Reidline or Readline
+ # +nil+:: uses stdin or Reline or Readline
# +String+:: uses a File
# +other+:: uses this as InputMethod
def initialize(irb, workspace = nil, input_method = nil)
@@ -32,7 +32,7 @@ module IRB
else
@workspace = WorkSpace.new
end
- @thread = Thread.current if defined? Thread
+ @thread = Thread.current
# copy of default configuration
@ap_name = IRB.conf[:AP_NAME]
@@ -48,7 +48,15 @@ module IRB
end
if IRB.conf.has_key?(:USE_MULTILINE)
@use_multiline = IRB.conf[:USE_MULTILINE]
- elsif IRB.conf.has_key?(:USE_REIDLINE) # backward compatibility
+ elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility
+ warn <<~MSG.strip
+ USE_RELINE is deprecated, please use USE_MULTILINE instead.
+ MSG
+ @use_multiline = IRB.conf[:USE_RELINE]
+ elsif IRB.conf.has_key?(:USE_REIDLINE)
+ warn <<~MSG.strip
+ USE_REIDLINE is deprecated, please use USE_MULTILINE instead.
+ MSG
@use_multiline = IRB.conf[:USE_REIDLINE]
else
@use_multiline = nil
@@ -83,14 +91,14 @@ module IRB
when nil
if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
# Both of multiline mode and singleline mode aren't specified.
- @io = ReidlineInputMethod.new
+ @io = RelineInputMethod.new
else
@io = nil
end
when false
@io = nil
when true
- @io = ReidlineInputMethod.new
+ @io = RelineInputMethod.new
end
unless @io
case use_singleline?
@@ -115,6 +123,10 @@ module IRB
end
@io = StdioInputMethod.new unless @io
+ when '-'
+ @io = FileInputMethod.new($stdin)
+ @irb_name = '-'
+ @irb_path = '-'
when String
@io = FileInputMethod.new(input_method)
@irb_name = File.basename(input_method)
@@ -140,6 +152,8 @@ module IRB
if @newline_before_multiline_output.nil?
@newline_before_multiline_output = true
end
+
+ @command_aliases = IRB.conf[:COMMAND_ALIASES]
end
# The top-level workspace, see WorkSpace#main
@@ -156,7 +170,7 @@ module IRB
# The current input method.
#
# Can be either StdioInputMethod, ReadlineInputMethod,
- # ReidlineInputMethod, FileInputMethod or other specified when the
+ # RelineInputMethod, FileInputMethod or other specified when the
# context is created. See ::new for more # information on +input_method+.
attr_accessor :io
@@ -317,14 +331,17 @@ module IRB
# See IRB@Command+line+options for more command line options.
attr_accessor :back_trace_limit
+ # User-defined IRB command aliases
+ attr_accessor :command_aliases
+
# Alias for #use_multiline
alias use_multiline? use_multiline
# Alias for #use_singleline
alias use_singleline? use_singleline
# backward compatibility
- alias use_reidline use_multiline
+ alias use_reline use_multiline
# backward compatibility
- alias use_reidline? use_multiline
+ alias use_reline? use_multiline
# backward compatibility
alias use_readline use_singleline
# backward compatibility
@@ -342,7 +359,7 @@ module IRB
# Returns whether messages are displayed or not.
def verbose?
if @verbose.nil?
- if @io.kind_of?(ReidlineInputMethod)
+ if @io.kind_of?(RelineInputMethod)
false
elsif defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
false
@@ -357,11 +374,11 @@ module IRB
end
# Whether #verbose? is +true+, and +input_method+ is either
- # StdioInputMethod or ReidlineInputMethod or ReadlineInputMethod, see #io
+ # StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io
# for more information.
def prompting?
verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
- @io.kind_of?(ReidlineInputMethod) ||
+ @io.kind_of?(RelineInputMethod) ||
(defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
end
@@ -468,6 +485,20 @@ module IRB
line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
@workspace.local_variable_set(:_, exception)
end
+
+ # Transform a non-identifier alias (@, $) or keywords (next, break)
+ command, args = line.split(/\s/, 2)
+ if original = command_aliases[command.to_sym]
+ line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
+ command = original
+ end
+
+ # Hook command-specific transformation
+ command_class = ExtendCommandBundle.load_command(command)
+ if command_class&.respond_to?(:transform_args)
+ line = "#{command} #{command_class.transform_args(args)}"
+ end
+
set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
end
@@ -509,5 +540,21 @@ module IRB
end
alias __to_s__ to_s
alias to_s inspect
+
+ def local_variables # :nodoc:
+ workspace.binding.local_variables
+ end
+
+ # Return true if it's aliased from the argument and it's not an identifier.
+ def symbol_alias?(command)
+ return nil if command.match?(/\A\w+\z/)
+ command_aliases.key?(command.to_sym)
+ end
+
+ # Return true if the command supports transforming args
+ def transform_args?(command)
+ command = command_aliases.fetch(command.to_sym, command)
+ ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
+ end
end
end
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 74de1ecde5..e57d43a569 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -9,7 +9,6 @@
#
#
#
-fail CantShiftToMultiIrbMode unless defined?(Thread)
module IRB
class JobManager
diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
index 7acaebe36a..9e7620545a 100644
--- a/lib/irb/ext/save-history.rb
+++ b/lib/irb/ext/save-history.rb
@@ -70,10 +70,10 @@ module IRB
end
history_file = IRB.rc_file("_history") unless history_file
if File.exist?(history_file)
- open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
+ File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
f.each { |l|
l = l.chomp
- if self.class == ReidlineInputMethod and history.last&.end_with?("\\")
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
history.last.delete_suffix!("\\")
history.last << "\n" << l
else
@@ -107,13 +107,13 @@ module IRB
raise
end
- if File.exist?(history_file) && @loaded_history_mtime &&
+ if File.exist?(history_file) &&
File.mtime(history_file) != @loaded_history_mtime
- history = history[@loaded_history_lines..-1]
+ history = history[@loaded_history_lines..-1] if @loaded_history_lines
append_history = true
end
- open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
+ File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
hist = history.map{ |l| l.split("\n").join("\\\n") }
unless append_history
begin
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 7778a0d0ce..d0829a06c4 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -45,14 +45,15 @@ module IRB # :nodoc:
[:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
]
+
@EXTEND_COMMANDS = [
[
:irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws",
+ [:cwws, NO_OVERRIDE],
+ [:pwws, NO_OVERRIDE],
[:irb_print_working_workspace, OVERRIDE_ALL],
[:irb_cwws, OVERRIDE_ALL],
[:irb_pwws, OVERRIDE_ALL],
- [:cwws, NO_OVERRIDE],
- [:pwws, NO_OVERRIDE],
[:irb_current_working_binding, OVERRIDE_ALL],
[:irb_print_working_binding, OVERRIDE_ALL],
[:irb_cwb, OVERRIDE_ALL],
@@ -60,10 +61,10 @@ module IRB # :nodoc:
],
[
:irb_change_workspace, :ChangeWorkspace, "cmd/chws",
- [:irb_chws, OVERRIDE_ALL],
- [:irb_cws, OVERRIDE_ALL],
[:chws, NO_OVERRIDE],
[:cws, NO_OVERRIDE],
+ [:irb_chws, OVERRIDE_ALL],
+ [:irb_cws, OVERRIDE_ALL],
[:irb_change_binding, OVERRIDE_ALL],
[:irb_cb, OVERRIDE_ALL],
[:cb, NO_OVERRIDE],
@@ -77,16 +78,16 @@ module IRB # :nodoc:
],
[
:irb_push_workspace, :PushWorkspace, "cmd/pushws",
- [:irb_pushws, OVERRIDE_ALL],
[:pushws, NO_OVERRIDE],
+ [:irb_pushws, OVERRIDE_ALL],
[:irb_push_binding, OVERRIDE_ALL],
[:irb_pushb, OVERRIDE_ALL],
[:pushb, NO_OVERRIDE],
],
[
:irb_pop_workspace, :PopWorkspace, "cmd/pushws",
- [:irb_popws, OVERRIDE_ALL],
[:popws, NO_OVERRIDE],
+ [:irb_popws, OVERRIDE_ALL],
[:irb_pop_binding, OVERRIDE_ALL],
[:irb_popb, OVERRIDE_ALL],
[:popb, NO_OVERRIDE],
@@ -117,12 +118,56 @@ module IRB # :nodoc:
],
[
+ :irb_debug, :Debug, "cmd/debug",
+ [:debug, NO_OVERRIDE],
+ ],
+ [
+ :irb_edit, :Edit, "cmd/edit",
+ [:edit, NO_OVERRIDE],
+ ],
+ [
+ :irb_break, :Break, "cmd/break",
+ ],
+ [
+ :irb_catch, :Catch, "cmd/catch",
+ ],
+ [
+ :irb_next, :Next, "cmd/next"
+ ],
+ [
+ :irb_delete, :Delete, "cmd/delete",
+ [:delete, NO_OVERRIDE],
+ ],
+ [
+ :irb_step, :Step, "cmd/step",
+ [:step, NO_OVERRIDE],
+ ],
+ [
+ :irb_continue, :Continue, "cmd/continue",
+ [:continue, NO_OVERRIDE],
+ ],
+ [
+ :irb_finish, :Finish, "cmd/finish",
+ [:finish, NO_OVERRIDE],
+ ],
+ [
+ :irb_backtrace, :Backtrace, "cmd/backtrace",
+ [:backtrace, NO_OVERRIDE],
+ [:bt, NO_OVERRIDE],
+ ],
+ [
+ :irb_debug_info, :Info, "cmd/info",
+ [:info, NO_OVERRIDE],
+ ],
+
+ [
:irb_help, :Help, "cmd/help",
+ [:show_doc, NO_OVERRIDE],
[:help, NO_OVERRIDE],
],
[
- :irb_info, :Info, "cmd/info"
+ :irb_info, :IrbInfo, "cmd/irb_info"
],
[
@@ -144,24 +189,56 @@ module IRB # :nodoc:
:irb_whereami, :Whereami, "cmd/whereami",
[:whereami, NO_OVERRIDE],
],
-
+ [
+ :irb_show_cmds, :ShowCmds, "cmd/show_cmds",
+ [:show_cmds, NO_OVERRIDE],
+ ]
]
- # Installs the default irb commands:
- #
- # +irb_current_working_workspace+:: Context#main
- # +irb_change_workspace+:: Context#change_workspace
- # +irb_workspaces+:: Context#workspaces
- # +irb_push_workspace+:: Context#push_workspace
- # +irb_pop_workspace+:: Context#pop_workspace
- # +irb_load+:: #irb_load
- # +irb_require+:: #irb_require
- # +irb_source+:: IrbLoader#source_file
- # +irb+:: IRB.irb
- # +irb_jobs+:: JobManager
- # +irb_fg+:: JobManager#switch
- # +irb_kill+:: JobManager#kill
- # +irb_help+:: IRB@Command+line+options
+
+ @@commands = []
+
+ def self.all_commands_info
+ return @@commands unless @@commands.empty?
+ user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
+ result[target] ||= []
+ result[target] << alias_name
+ end
+
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
+ require_relative load_file
+ end
+
+ klass = ExtendCommand.const_get(cmd_class, false)
+ aliases = aliases.map { |a| a.first }
+
+ if additional_aliases = user_aliases[cmd_name]
+ aliases += additional_aliases
+ end
+
+ display_name = aliases.shift || cmd_name
+ @@commands << { display_name: display_name, description: klass.description, category: klass.category }
+ end
+
+ @@commands
+ end
+
+ # Convert a command name to its implementation class if such command exists
+ def self.load_command(command)
+ command = command.to_sym
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
+ next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
+
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
+ require_relative load_file
+ end
+ return ExtendCommand.const_get(cmd_class, false)
+ end
+ nil
+ end
+
+ # Installs the default irb commands.
def self.install_extend_commands
for args in @EXTEND_COMMANDS
def_extend_command(*args)
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index d2baee2017..55453cc8f7 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -44,8 +44,8 @@ module IRB # :nodoc:
@CONF[:IRB_RC] = nil
@CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
- @CONF[:USE_COLORIZE] = !ENV['NO_COLOR']
- @CONF[:USE_AUTOCOMPLETE] = true
+ @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
+ @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
@CONF[:INSPECT_MODE] = true
@CONF[:USE_TRACER] = false
@CONF[:USE_LOADER] = false
@@ -158,6 +158,16 @@ module IRB # :nodoc:
@CONF[:LC_MESSAGES] = Locale.new
@CONF[:AT_EXIT] = []
+
+ @CONF[:COMMAND_ALIASES] = {
+ # Symbol aliases
+ :'$' => :show_source,
+ :'@' => :whereami,
+ # Keyword aliases
+ :break => :irb_break,
+ :catch => :irb_catch,
+ :next => :irb_next,
+ }
end
def IRB.set_measure_callback(type = nil, arg = nil, &block)
@@ -255,8 +265,20 @@ module IRB # :nodoc:
when "--nosingleline", "--noreadline"
@CONF[:USE_SINGLELINE] = false
when "--multiline", "--reidline"
+ if opt == "--reidline"
+ warn <<~MSG.strip
+ --reidline is deprecated, please use --multiline instead.
+ MSG
+ end
+
@CONF[:USE_MULTILINE] = true
when "--nomultiline", "--noreidline"
+ if opt == "--noreidline"
+ warn <<~MSG.strip
+ --noreidline is deprecated, please use --nomultiline instead.
+ MSG
+ end
+
@CONF[:USE_MULTILINE] = false
when /^--extra-doc-dir(?:=(.+))?/
opt = $1 || argv.shift
@@ -289,6 +311,10 @@ module IRB # :nodoc:
@CONF[:PROMPT_MODE] = prompt_mode
when "--noprompt"
@CONF[:PROMPT_MODE] = :NULL
+ when "--script"
+ noscript = false
+ when "--noscript"
+ noscript = true
when "--inf-ruby-mode"
@CONF[:PROMPT_MODE] = :INF_RUBY
when "--sample-book-mode", "--simple-prompt"
@@ -309,16 +335,20 @@ module IRB # :nodoc:
IRB.print_usage
exit 0
when "--"
- if opt = argv.shift
+ if !noscript && (opt = argv.shift)
@CONF[:SCRIPT] = opt
$0 = opt
end
break
- when /^-/
+ when /^-./
fail UnrecognizedSwitch, opt
else
- @CONF[:SCRIPT] = opt
- $0 = opt
+ if noscript
+ argv.unshift(opt)
+ else
+ @CONF[:SCRIPT] = opt
+ $0 = opt
+ end
break
end
end
@@ -371,11 +401,9 @@ module IRB # :nodoc:
end
if xdg_config_home = ENV["XDG_CONFIG_HOME"]
irb_home = File.join(xdg_config_home, "irb")
- unless File.exist? irb_home
- require 'fileutils'
- FileUtils.mkdir_p irb_home
+ if File.directory?(irb_home)
+ yield proc{|rc| irb_home + "/irb#{rc}"}
end
- yield proc{|rc| irb_home + "/irb#{rc}"}
end
if home = ENV["HOME"]
yield proc{|rc| home+"/.irb#{rc}"}
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index fd68239ee3..9480573195 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -14,7 +14,6 @@ require_relative 'magic-file'
require_relative 'completion'
require 'io/console'
require 'reline'
-require 'rdoc'
module IRB
STDIN_FILE_NAME = "(line)" # :nodoc:
@@ -138,7 +137,7 @@ module IRB
# Creates a new input method object
def initialize(file)
super
- @io = IRB::MagicFile.open(file)
+ @io = file.is_a?(IO) ? file : IRB::MagicFile.open(file)
@external_encoding = @io.external_encoding
end
# The file name of this input method, usually given during initialization.
@@ -262,7 +261,7 @@ module IRB
end
end
- class ReidlineInputMethod < InputMethod
+ class RelineInputMethod < InputMethod
include Reline
# Creates a new input method object using Reline
@@ -287,7 +286,8 @@ module IRB
if IRB.conf[:USE_COLORIZE]
proc do |output, complete: |
next unless IRB::Color.colorable?
- IRB::Color.colorize_code(output, complete: complete)
+ lvars = IRB.CurrentContext&.local_variables || []
+ IRB::Color.colorize_code(output, complete: complete, local_variables: lvars)
end
else
proc do |output|
@@ -296,8 +296,13 @@ module IRB
end
Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
+
if IRB.conf[:USE_AUTOCOMPLETE]
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
+ begin
+ require 'rdoc'
+ Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
+ rescue LoadError
+ end
end
end
@@ -456,7 +461,7 @@ module IRB
# For debug message
def inspect
config = Reline::Config.new
- str = "ReidlineInputMethod with Reline #{Reline::VERSION}"
+ str = "RelineInputMethod with Reline #{Reline::VERSION}"
if config.respond_to?(:inputrc_path)
inputrc_path = File.expand_path(config.inputrc_path)
else
@@ -466,4 +471,13 @@ module IRB
str
end
end
+
+ class ReidlineInputMethod < RelineInputMethod
+ def initialize
+ warn <<~MSG.strip
+ IRB::ReidlineInputMethod is deprecated, please use IRB::RelineInputMethod instead.
+ MSG
+ super
+ end
+ end
end
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index 26d0fb018f..c3e8a4dc58 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -8,8 +8,8 @@ end
Gem::Specification.new do |spec|
spec.name = "irb"
spec.version = IRB::VERSION
- spec.authors = ["Keiju ISHITSUKA"]
- spec.email = ["keiju@ruby-lang.org"]
+ spec.authors = ["aycabta", "Keiju ISHITSUKA"]
+ spec.email = ["aycabta@gmail.com", "keiju@ruby-lang.org"]
spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6")
spec.add_dependency "reline", ">= 0.3.0"
end
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
index b8a7fe5a0e..cb5c21cdb4 100644
--- a/lib/irb/lc/error.rb
+++ b/lib/irb/lc/error.rb
@@ -48,11 +48,6 @@ module IRB
super("No such job(#{val}).")
end
end
- class CantShiftToMultiIrbMode < StandardError
- def initialize
- super("Can't shift to multi irb mode.")
- end
- end
class CantChangeBinding < StandardError
def initialize(val)
super("Can't change binding to (#{val}).")
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index 3405fa775f..5b23f4c41e 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -38,6 +38,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--sample-book-mode, --simple-prompt
Set prompt mode to 'simple'.
--noprompt Don't output prompt.
+ --script Script mode (default, treat first argument as script)
+ --noscript No script mode (leave arguments in argv)
--single-irb Share self with sub-irb.
--tracer Show stack trace for each command.
--back-trace-limit n[=16]
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
index d7c181c02e..5e3622cbae 100644
--- a/lib/irb/lc/ja/error.rb
+++ b/lib/irb/lc/ja/error.rb
@@ -48,11 +48,6 @@ module IRB
super("そのようなジョブ(#{val})はありません.")
end
end
- class CantShiftToMultiIrbMode < StandardError
- def initialize
- super("multi-irb modeに移れません.")
- end
- end
class CantChangeBinding < StandardError
def initialize(val)
super("バインディング(#{val})に変更できません.")
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 29862f5507..85b336fbe1 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -48,7 +48,7 @@ class RubyLex
end
# io functions
- def set_input(io, p = nil, context: nil, &block)
+ def set_input(io, p = nil, context:, &block)
@io = io
if @io.respond_to?(:check_termination)
@io.check_termination do |code|
@@ -65,6 +65,12 @@ class RubyLex
false
end
else
+ # Accept any single-line input for symbol aliases or commands that transform args
+ command = code.split(/\s/, 2).first
+ if context.symbol_alias?(command) || context.transform_args?(command)
+ next true
+ end
+
code.gsub!(/\s*\z/, '').concat("\n")
ltype, indent, continue, code_block_open = check_state(code, context: context)
if ltype or indent > 0 or continue or code_block_open
@@ -136,41 +142,37 @@ class RubyLex
:on_param_error
]
+ def self.generate_local_variables_assign_code(local_variables)
+ "#{local_variables.join('=')}=nil;" unless local_variables.empty?
+ end
+
def self.ripper_lex_without_warning(code, context: nil)
verbose, $VERBOSE = $VERBOSE, nil
- if context
- lvars = context&.workspace&.binding&.local_variables
- if lvars && !lvars.empty?
- code = "#{lvars.join('=')}=nil\n#{code}"
- line_no = 0
- else
- line_no = 1
- end
+ lvars_code = generate_local_variables_assign_code(context&.local_variables || [])
+ if lvars_code
+ code = "#{lvars_code}\n#{code}"
+ line_no = 0
+ else
+ line_no = 1
end
- tokens = nil
+
compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
if lexer.respond_to?(:scan) # Ruby 2.7+
- tokens = []
- pos_to_index = {}
- lexer.scan.each do |t|
+ lexer.scan.each_with_object([]) do |t, tokens|
next if t.pos.first == 0
- if pos_to_index.has_key?(t.pos)
- index = pos_to_index[t.pos]
- found_tk = tokens[index]
- if ERROR_TOKENS.include?(found_tk.event) && !ERROR_TOKENS.include?(t.event)
- tokens[index] = t
- end
+ prev_tk = tokens.last
+ position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
+ if position_overlapped
+ tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
else
- pos_to_index[t.pos] = tokens.size
tokens << t
end
end
else
- tokens = lexer.parse.reject { |it| it.pos.first == 0 }
+ lexer.parse.reject { |it| it.pos.first == 0 }.sort_by(&:pos)
end
end
- tokens
ensure
$VERBOSE = verbose
end
@@ -209,12 +211,7 @@ class RubyLex
last_line = lines[line_index]&.byteslice(0, byte_pointer)
code += last_line if last_line
@tokens = self.class.ripper_lex_without_warning(code, context: context)
- corresponding_token_depth = check_corresponding_token_depth(lines, line_index)
- if corresponding_token_depth
- corresponding_token_depth
- else
- nil
- end
+ check_corresponding_token_depth(lines, line_index)
end
end
end
@@ -225,6 +222,8 @@ class RubyLex
ltype = process_literal_type(tokens)
indent = process_nesting_level(tokens)
continue = process_continue(tokens)
+ lvars_code = self.class.generate_local_variables_assign_code(context.local_variables)
+ code = "#{lvars_code}\n#{code}" if lvars_code
code_block_open = check_code_block(code, tokens)
[ltype, indent, continue, code_block_open]
end
@@ -244,13 +243,13 @@ class RubyLex
@code_block_open = false
end
- def each_top_level_statement
+ def each_top_level_statement(context)
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
- unless l = lex
+ unless l = lex(context)
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
@@ -280,18 +279,15 @@ class RubyLex
end
end
- def lex
+ def lex(context)
line = @input.call
if @io.respond_to?(:check_termination)
return line # multiline
end
code = @line + (line.nil? ? '' : line)
code.gsub!(/\s*\z/, '').concat("\n")
- @tokens = self.class.ripper_lex_without_warning(code)
- @continue = process_continue
- @code_block_open = check_code_block(code)
- @indent = process_nesting_level
- @ltype = process_literal_type
+ @tokens = self.class.ripper_lex_without_warning(code, context: context)
+ @ltype, @indent, @continue, @code_block_open = check_state(code, @tokens, context: context)
line
end
@@ -308,7 +304,7 @@ class RubyLex
return true
elsif tokens.size >= 1 and tokens[-1].event == :on_heredoc_end # "EOH\n"
return false
- elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2].state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2].tok !~ /\A\.\.\.?\z/
+ elsif tokens.size >= 2 and tokens[-2].state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2].tok !~ /\A\.\.\.?\z/
# end of literal except for regexp
# endless range at end of line is not a continue
return true
@@ -389,21 +385,20 @@ class RubyLex
$VERBOSE = verbose
end
- if defined?(Ripper::EXPR_BEG)
- last_lex_state = tokens.last.state
- if last_lex_state.allbits?(Ripper::EXPR_BEG)
- return false
- elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
- return false
- end
+ last_lex_state = tokens.last.state
+
+ if last_lex_state.allbits?(Ripper::EXPR_BEG)
+ return false
+ elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
+ return false
end
false
@@ -718,6 +713,7 @@ class RubyLex
i = 0
start_token = []
end_type = []
+ pending_heredocs = []
while i < tokens.size
t = tokens[i]
case t.event
@@ -741,18 +737,27 @@ class RubyLex
end
end
when :on_backtick
- start_token << t
- end_type << :on_tstring_end
+ if t.state.allbits?(Ripper::EXPR_BEG)
+ start_token << t
+ end_type << :on_tstring_end
+ end
when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
start_token << t
end_type << :on_tstring_end
when :on_heredoc_beg
- start_token << t
- end_type << :on_heredoc_end
+ pending_heredocs << t
+ end
+
+ if pending_heredocs.any? && t.tok.include?("\n")
+ pending_heredocs.reverse_each do |t|
+ start_token << t
+ end_type << :on_heredoc_end
+ end
+ pending_heredocs = []
end
i += 1
end
- start_token.last.nil? ? nil : start_token.last
+ pending_heredocs.first || start_token.last
end
def process_literal_type(tokens = @tokens)
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 481d14ffd2..d1c0e54fdc 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -11,7 +11,7 @@
#
module IRB # :nodoc:
- VERSION = "1.4.1"
+ VERSION = "1.6.2"
@RELEASE_VERSION = VERSION
- @LAST_UPDATE_DATE = "2021-12-25"
+ @LAST_UPDATE_DATE = "2022-12-13"
end
diff --git a/lib/logger.rb b/lib/logger.rb
index cbb0da676b..7e4dacc911 100644
--- a/lib/logger.rb
+++ b/lib/logger.rb
@@ -107,7 +107,7 @@ require_relative 'logger/errors'
#
# - \Severity (one letter).
# - Timestamp.
-# - Timezone.
+# - Process id.
# - \Severity (word).
# - Program name.
# - Message.
@@ -147,7 +147,7 @@ require_relative 'logger/errors'
# when the entry is created.
#
# The logged timestamp is formatted by method
-# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]
+# {Time#strftime}[rdoc-ref:Time#strftime]
# using this format string:
#
# '%Y-%m-%dT%H:%M:%S.%6N'
@@ -365,7 +365,7 @@ require_relative 'logger/errors'
# You can set a different format using create-time option
# +shift_period_suffix+;
# see details and suggestions at
-# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
+# {Time#strftime}[rdoc-ref:Time#strftime].
#
class Logger
_, name, rev = %w$Id$
@@ -425,7 +425,7 @@ class Logger
# Argument +datetime_format+ should be either of these:
#
# - A string suitable for use as a format for method
- # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime].
+ # {Time#strftime}[rdoc-ref:Time#strftime].
# - +nil+: the logger uses <tt>'%Y-%m-%dT%H:%M:%S.%6N'</tt>.
#
def datetime_format=(datetime_format)
@@ -453,7 +453,7 @@ class Logger
# The proc should return a string containing the formatted entry.
#
# This custom formatter uses
- # {String#dump}[https://docs.ruby-lang.org/en/master/String.html#method-i-dump]
+ # {String#dump}[rdoc-ref:String#dump]
# to escape the message string:
#
# logger = Logger.new($stdout, progname: 'mung')
diff --git a/lib/logger/formatter.rb b/lib/logger/formatter.rb
index 34e07bc57f..c634dbf34d 100644
--- a/lib/logger/formatter.rb
+++ b/lib/logger/formatter.rb
@@ -3,7 +3,7 @@
class Logger
# Default formatter for log messages.
class Formatter
- Format = "%s, [%s #%d] %5s -- %s: %s\n"
+ Format = "%.1s, [%s #%d] %5s -- %s: %s\n"
DatetimeFormat = "%Y-%m-%dT%H:%M:%S.%6N"
attr_accessor :datetime_format
@@ -13,8 +13,7 @@ class Logger
end
def call(severity, time, progname, msg)
- Format % [severity[0, 1], format_datetime(time), Process.pid, severity, progname,
- msg2str(msg)]
+ sprintf(Format, severity, format_datetime(time), Process.pid, severity, progname, msg2str(msg))
end
private
diff --git a/lib/logger/log_device.rb b/lib/logger/log_device.rb
index 8683328a5e..84277a2656 100644
--- a/lib/logger/log_device.rb
+++ b/lib/logger/log_device.rb
@@ -79,8 +79,10 @@ class Logger
def set_dev(log)
if log.respond_to?(:write) and log.respond_to?(:close)
@dev = log
- if log.respond_to?(:path)
- @filename = log.path
+ if log.respond_to?(:path) and path = log.path
+ if File.exist?(path)
+ @filename = path
+ end
end
else
@dev = open_logfile(log)
diff --git a/lib/logger/logger.gemspec b/lib/logger/logger.gemspec
index ccd4e70db7..d12db625d9 100644
--- a/lib/logger/logger.gemspec
+++ b/lib/logger/logger.gemspec
@@ -23,5 +23,4 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", ">= 0"
spec.add_development_dependency "rake", ">= 12.3.3"
spec.add_development_dependency "test-unit"
- spec.add_development_dependency "rdoc"
end
diff --git a/lib/logger/version.rb b/lib/logger/version.rb
index ba845a9304..f85c72eed3 100644
--- a/lib/logger/version.rb
+++ b/lib/logger/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class Logger
- VERSION = "1.5.1"
+ VERSION = "1.5.3"
end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index efe3419fd7..0fbc1cc2e5 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -1762,7 +1762,7 @@ SRC
hdr << "#endif\n"
hdr = hdr.join("")
log_src(hdr, "#{header} is")
- unless (IO.read(header) == hdr rescue false)
+ unless (File.read(header) == hdr rescue false)
File.open(header, "wb") do |hfile|
hfile.write(hdr)
end
@@ -1866,7 +1866,7 @@ SRC
if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
# if and only if package specific config command is given
elsif ($PKGCONFIG ||=
- (pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) &&
+ (pkgconfig = with_config("pkg-config") {config_string("PKG_CONFIG") || "pkg-config"}) &&
find_executable0(pkgconfig) && pkgconfig) and
xsystem([*envs, $PKGCONFIG, "--exists", pkg])
# default to pkg-config command
@@ -2378,11 +2378,19 @@ TIMESTAMP_DIR = #{$extout && $extmk ? '$(extout)/.timestamp' : '.'}
install_dirs.each {|d| conf << ("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]}
sodir = $extout ? '$(TARGET_SO_DIR)' : '$(RUBYARCHDIR)'
n = '$(TARGET_SO_DIR)$(TARGET)'
+ cleanobjs = ["$(OBJS)"]
+ if $extmk
+ %w[bc i s].each {|ex| cleanobjs << "$(OBJS:.#{$OBJEXT}=.#{ex})"}
+ end
+ if target
+ config_string('cleanobjs') {|t| cleanobjs << t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")}
+ end
conf << "\
TARGET_SO_DIR =#{$extout ? " $(RUBYARCHDIR)/" : ''}
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = #{'$(TARGET_SO) ' if target}#{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}}
-CLEANOBJS = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")} if target} *.bak
+CLEANOBJS = #{cleanobjs.join(' ')} *.bak
+TARGET_SO_DIR_TIMESTAMP = #{timestamp_file(sodir, target_prefix)}
" #"
conf = yield(conf) if block_given?
@@ -2416,7 +2424,7 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
if target
f = "$(DLLIB)"
dest = "$(TARGET_SO)"
- stamp = timestamp_file(dir, target_prefix)
+ stamp = '$(TARGET_SO_DIR_TIMESTAMP)'
if $extout
mfile.puts dest
mfile.print "clean-so::\n"
@@ -2485,7 +2493,9 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
end
end
end
- dirs.unshift(sodir) if target and !dirs.include?(sodir)
+ if target and !dirs.include?(sodir)
+ mfile.print "$(TARGET_SO_DIR_TIMESTAMP):\n\t$(Q) $(MAKEDIRS) $(@D) #{sodir}\n\t$(Q) $(TOUCH) $@\n"
+ end
dirs.each do |d|
t = timestamp_file(d, target_prefix)
mfile.print "#{t}:\n\t$(Q) $(MAKEDIRS) $(@D) #{d}\n\t$(Q) $(TOUCH) $@\n"
@@ -2529,7 +2539,7 @@ site-install-rb: install-rb
mfile.print "$(TARGET_SO): "
mfile.print "$(DEFFILE) " if makedef
mfile.print "$(OBJS) Makefile"
- mfile.print " #{timestamp_file(sodir, target_prefix)}" if $extout
+ mfile.print " $(TARGET_SO_DIR_TIMESTAMP)" if $extout
mfile.print "\n"
mfile.print "\t$(ECHO) linking shared-object #{target_prefix.sub(/\A\/(.*)/, '\1/')}$(DLLIB)\n"
mfile.print "\t-$(Q)$(RM) $(@#{sep})\n"
@@ -2599,6 +2609,7 @@ site-install-rb: install-rb
$INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk
$INCFLAGS << " -I$(hdrdir) -I$(srcdir)"
$DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup
+ config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk
$LIBEXT = config['LIBEXT'].dup
$OBJEXT = config["OBJEXT"].dup
$EXEEXT = config["EXEEXT"].dup
diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb
index abd0fc6add..17ec9924e4 100644
--- a/lib/mutex_m.rb
+++ b/lib/mutex_m.rb
@@ -40,7 +40,7 @@
#
module Mutex_m
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
Ractor.make_shareable(VERSION) if defined?(Ractor)
def Mutex_m.define_aliases(cl) # :nodoc:
diff --git a/lib/net/http.rb b/lib/net/http.rb
index a583441253..387df4b8f4 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = net/http.rb
#
@@ -32,373 +32,697 @@ module Net #:nodoc:
class HTTPHeaderSyntaxError < StandardError; end
# :startdoc:
- # == An HTTP client API for Ruby.
+ # \Class \Net::HTTP provides a rich library that implements the client
+ # in a client-server model that uses the \HTTP request-response protocol.
+ # For information about \HTTP, see:
+ #
+ # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol].
+ # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview].
+ #
+ # == About the Examples
+ #
+ # :include: doc/net-http/examples.rdoc
+ #
+ # == Strategies
+ #
+ # - If you will make only a few GET requests,
+ # consider using {OpenURI}[rdoc-ref:OpenURI].
+ # - If you will make only a few requests of all kinds,
+ # consider using the various singleton convenience methods in this class.
+ # Each of the following methods automatically starts and finishes
+ # a {session}[rdoc-ref:Net::HTTP@Sessions] that sends a single request:
+ #
+ # # Return string response body.
+ # Net::HTTP.get(hostname, path)
+ # Net::HTTP.get(uri)
+ #
+ # # Write string response body to $stdout.
+ # Net::HTTP.get_print(hostname, path)
+ # Net::HTTP.get_print(uri)
+ #
+ # # Return response as Net::HTTPResponse object.
+ # Net::HTTP.get_response(hostname, path)
+ # Net::HTTP.get_response(uri)
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # Net::HTTP.post(uri, data)
+ # params = {title: 'foo', body: 'bar', userId: 1}
+ # Net::HTTP.post_form(uri, params)
+ #
+ # - If performance is important, consider using sessions, which lower request overhead.
+ # This {session}[rdoc-ref:Net::HTTP@Sessions] has multiple requests for
+ # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]
+ # and {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
+ #
+ # Net::HTTP.start(hostname) do |http|
+ # # Session started automatically before block execution.
+ # http.get(path)
+ # http.head(path)
+ # body = 'Some text'
+ # http.post(path, body) # Can also have a block.
+ # http.put(path, body)
+ # http.delete(path)
+ # http.options(path)
+ # http.trace(path)
+ # http.patch(path, body) # Can also have a block.
+ # http.copy(path)
+ # http.lock(path, body)
+ # http.mkcol(path, body)
+ # http.move(path)
+ # http.propfind(path, body)
+ # http.proppatch(path, body)
+ # http.unlock(path, body)
+ # # Session finished automatically at block exit.
+ # end
#
- # Net::HTTP provides a rich library which can be used to build HTTP
- # user-agents. For more details about HTTP see
- # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt).
+ # The methods cited above are convenience methods that, via their few arguments,
+ # allow minimal control over the requests.
+ # For greater control, consider using {request objects}[rdoc-ref:Net::HTTPRequest].
#
- # Net::HTTP is designed to work closely with URI. URI::HTTP#host,
- # URI::HTTP#port and URI::HTTP#request_uri are designed to work with
- # Net::HTTP.
+ # == URIs
#
- # If you are only performing a few GET requests you should try OpenURI.
+ # On the internet, a URI
+ # ({Universal Resource Identifier}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier])
+ # is a string that identifies a particular resource.
+ # It consists of some or all of: scheme, hostname, path, query, and fragment;
+ # see {URI syntax}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax].
#
- # == Simple Examples
+ # A Ruby {URI::Generic}[rdoc-ref:URI::Generic] object
+ # represents an internet URI.
+ # It provides, among others, methods
+ # +scheme+, +hostname+, +path+, +query+, and +fragment+.
#
- # All examples assume you have loaded Net::HTTP with:
+ # === Schemes
#
- # require 'net/http'
+ # An internet \URI has
+ # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes].
#
- # This will also require 'uri' so you don't need to require it separately.
+ # The two schemes supported in \Net::HTTP are <tt>'https'</tt> and <tt>'http'</tt>:
#
- # The Net::HTTP methods in the following section do not persist
- # connections. They are not recommended if you are performing many HTTP
- # requests.
+ # uri.scheme # => "https"
+ # URI('http://example.com').scheme # => "http"
#
- # === GET
+ # === Hostnames
#
- # Net::HTTP.get('example.com', '/index.html') # => String
+ # A hostname identifies a server (host) to which requests may be sent:
#
- # === GET by URI
+ # hostname = uri.hostname # => "jsonplaceholder.typicode.com"
+ # Net::HTTP.start(hostname) do |http|
+ # # Some HTTP stuff.
+ # end
#
- # uri = URI('http://example.com/index.html?count=10')
- # Net::HTTP.get(uri) # => String
+ # === Paths
#
- # === GET with Dynamic Parameters
+ # A host-specific path identifies a resource on the host:
#
- # uri = URI('http://example.com/index.html')
- # params = { :limit => 10, :page => 3 }
- # uri.query = URI.encode_www_form(params)
+ # _uri = uri.dup
+ # _uri.path = '/todos/1'
+ # hostname = _uri.hostname
+ # path = _uri.path
+ # Net::HTTP.get(hostname, path)
#
- # res = Net::HTTP.get_response(uri)
- # puts res.body if res.is_a?(Net::HTTPSuccess)
+ # === Queries
#
- # === POST
+ # A host-specific query adds name/value pairs to the URI:
#
- # uri = URI('http://www.example.com/search.cgi')
- # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
- # puts res.body
+ # _uri = uri.dup
+ # params = {userId: 1, completed: false}
+ # _uri.query = URI.encode_www_form(params)
+ # _uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com?userId=1&completed=false>
+ # Net::HTTP.get(_uri)
#
- # === POST with Multiple Values
+ # === Fragments
#
- # uri = URI('http://www.example.com/search.cgi')
- # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
- # puts res.body
+ # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect
+ # in \Net::HTTP;
+ # the same data is returned, regardless of whether a fragment is included.
#
- # == How to use Net::HTTP
+ # == Request Headers
#
- # The following example code can be used as the basis of an HTTP user-agent
- # which can perform a variety of request types using persistent
- # connections.
+ # Request headers may be used to pass additional information to the host,
+ # similar to arguments passed in a method call;
+ # each header is a name/value pair.
#
- # uri = URI('http://example.com/some_path?query=string')
+ # Each of the \Net::HTTP methods that sends a request to the host
+ # has optional argument +headers+,
+ # where the headers are expressed as a hash of field-name/value pairs:
#
- # Net::HTTP.start(uri.host, uri.port) do |http|
- # request = Net::HTTP::Get.new uri
+ # headers = {Accept: 'application/json', Connection: 'Keep-Alive'}
+ # Net::HTTP.get(uri, headers)
#
- # response = http.request request # Net::HTTPResponse object
- # end
+ # See lists of both standard request fields and common request fields at
+ # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+ # A host may also accept other custom fields.
#
- # Net::HTTP::start immediately creates a connection to an HTTP server which
- # is kept open for the duration of the block. The connection will remain
- # open for multiple requests in the block if the server indicates it
- # supports persistent connections.
+ # == \HTTP Sessions
#
- # If you wish to re-use a connection across multiple HTTP requests without
- # automatically closing it you can use ::new and then call #start and
- # #finish manually.
+ # A _session_ is a connection between a server (host) and a client that:
#
- # The request types Net::HTTP supports are listed below in the section "HTTP
- # Request Classes".
+ # - Is begun by instance method Net::HTTP#start.
+ # - May contain any number of requests.
+ # - Is ended by instance method Net::HTTP#finish.
#
- # For all the Net::HTTP request objects and shortcut request methods you may
- # supply either a String for the request path or a URI from which Net::HTTP
- # will extract the request path.
+ # See example sessions at {Strategies}[rdoc-ref:Net::HTTP@Strategies].
#
- # === Response Data
+ # === Session Using \Net::HTTP.start
#
- # uri = URI('http://example.com/index.html')
- # res = Net::HTTP.get_response(uri)
+ # If you have many requests to make to a single host (and port),
+ # consider using singleton method Net::HTTP.start with a block;
+ # the method handles the session automatically by:
#
- # # Headers
- # res['Set-Cookie'] # => String
- # res.get_fields('set-cookie') # => Array
- # res.to_hash['set-cookie'] # => Array
- # puts "Headers: #{res.to_hash.inspect}"
+ # - Calling #start before block execution.
+ # - Executing the block.
+ # - Calling #finish after block execution.
#
- # # Status
- # puts res.code # => '200'
- # puts res.message # => 'OK'
- # puts res.class.name # => 'HTTPOK'
+ # In the block, you can use these instance methods,
+ # each of which that sends a single request:
#
- # # Body
- # puts res.body
+ # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
#
- # === Following Redirection
+ # - #get, #request_get: GET.
+ # - #head, #request_head: HEAD.
+ # - #post, #request_post: POST.
+ # - #delete: DELETE.
+ # - #options: OPTIONS.
+ # - #trace: TRACE.
+ # - #patch: PATCH.
#
- # Each Net::HTTPResponse object belongs to a class for its response code.
+ # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
#
- # For example, all 2XX responses are instances of a Net::HTTPSuccess
- # subclass, a 3XX response is an instance of a Net::HTTPRedirection
- # subclass and a 200 response is an instance of the Net::HTTPOK class. For
- # details of response classes, see the section "HTTP Response Classes"
- # below.
+ # - #copy: COPY.
+ # - #lock: LOCK.
+ # - #mkcol: MKCOL.
+ # - #move: MOVE.
+ # - #propfind: PROPFIND.
+ # - #proppatch: PROPPATCH.
+ # - #unlock: UNLOCK.
#
- # Using a case statement you can handle various types of responses properly:
+ # === Session Using \Net::HTTP.start and \Net::HTTP.finish
#
- # def fetch(uri_str, limit = 10)
- # # You should choose a better exception.
- # raise ArgumentError, 'too many HTTP redirects' if limit == 0
+ # You can manage a session manually using methods #start and #finish:
#
- # response = Net::HTTP.get_response(URI(uri_str))
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.get('/todos/1')
+ # http.get('/todos/2')
+ # http.delete('/posts/1')
+ # http.finish # Needed to free resources.
#
- # case response
- # when Net::HTTPSuccess then
- # response
- # when Net::HTTPRedirection then
- # location = response['location']
- # warn "redirected to #{location}"
- # fetch(location, limit - 1)
- # else
- # response.value
- # end
- # end
+ # === Single-Request Session
#
- # print fetch('http://www.ruby-lang.org')
+ # Certain convenience methods automatically handle a session by:
#
- # === POST
+ # - Creating an \HTTP object
+ # - Starting a session.
+ # - Sending a single request.
+ # - Finishing the session.
+ # - Destroying the object.
#
- # A POST can be made using the Net::HTTP::Post request class. This example
- # creates a URL encoded POST body:
+ # Such methods that send GET requests:
#
- # uri = URI('http://www.example.com/todo.cgi')
- # req = Net::HTTP::Post.new(uri)
- # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31')
+ # - ::get: Returns the string response body.
+ # - ::get_print: Writes the string response body to $stdout.
+ # - ::get_response: Returns a Net::HTTPResponse object.
#
- # res = Net::HTTP.start(uri.hostname, uri.port) do |http|
- # http.request(req)
- # end
+ # Such methods that send POST requests:
#
- # case res
- # when Net::HTTPSuccess, Net::HTTPRedirection
- # # OK
- # else
- # res.value
- # end
+ # - ::post: Posts data to the host.
+ # - ::post_form: Posts form data to the host.
#
- # To send multipart/form-data use Net::HTTPHeader#set_form:
+ # == \HTTP Requests and Responses
#
- # req = Net::HTTP::Post.new(uri)
- # req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data')
+ # Many of the methods above are convenience methods,
+ # each of which sends a request and returns a string
+ # without directly using \Net::HTTPRequest and \Net::HTTPResponse objects.
#
- # Other requests that can contain a body such as PUT can be created in the
- # same way using the corresponding request class (Net::HTTP::Put).
+ # You can, however, directly create a request object, send the request,
+ # and retrieve the response object; see:
#
- # === Setting Headers
+ # - Net::HTTPRequest.
+ # - Net::HTTPResponse.
#
- # The following example performs a conditional GET using the
- # If-Modified-Since header. If the files has not been modified since the
- # time in the header a Not Modified response will be returned. See RFC 2616
- # section 9.3 for further details.
+ # == Following Redirection
#
- # uri = URI('http://example.com/cached_response')
- # file = File.stat 'cached_response'
+ # Each returned response is an instance of a subclass of Net::HTTPResponse.
+ # See the {response class hierarchy}[rdoc-ref:Net::HTTPResponse@Response+Subclasses].
#
- # req = Net::HTTP::Get.new(uri)
- # req['If-Modified-Since'] = file.mtime.rfc2822
+ # In particular, class Net::HTTPRedirection is the parent
+ # of all redirection classes.
+ # This allows you to craft a case statement to handle redirections properly:
#
- # res = Net::HTTP.start(uri.hostname, uri.port) {|http|
- # http.request(req)
- # }
+ # def fetch(uri, limit = 10)
+ # # You should choose a better exception.
+ # raise ArgumentError, 'Too many HTTP redirects' if limit == 0
+ #
+ # res = Net::HTTP.get_response(URI(uri))
+ # case res
+ # when Net::HTTPSuccess # Any success class.
+ # res
+ # when Net::HTTPRedirection # Any redirection class.
+ # location = res['Location']
+ # warn "Redirected to #{location}"
+ # fetch(location, limit - 1)
+ # else # Any other class.
+ # res.value
+ # end
+ # end
#
- # open 'cached_response', 'w' do |io|
- # io.write res.body
- # end if res.is_a?(Net::HTTPSuccess)
+ # fetch(uri)
#
- # === Basic Authentication
+ # == Basic Authentication
#
# Basic authentication is performed according to
- # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
- #
- # uri = URI('http://example.com/index.html?key=value')
+ # {RFC2617}[http://www.ietf.org/rfc/rfc2617.txt]:
#
# req = Net::HTTP::Get.new(uri)
- # req.basic_auth 'user', 'pass'
- #
- # res = Net::HTTP.start(uri.hostname, uri.port) {|http|
+ # req.basic_auth('user', 'pass')
+ # res = Net::HTTP.start(hostname) do |http|
# http.request(req)
- # }
- # puts res.body
+ # end
#
- # === Streaming Response Bodies
+ # == Streaming Response Bodies
#
- # By default Net::HTTP reads an entire response into memory. If you are
+ # By default \Net::HTTP reads an entire response into memory. If you are
# handling large files or wish to implement a progress bar you can instead
# stream the body directly to an IO.
#
- # uri = URI('http://example.com/large_file')
- #
- # Net::HTTP.start(uri.host, uri.port) do |http|
- # request = Net::HTTP::Get.new uri
- #
- # http.request request do |response|
- # open 'large_file', 'w' do |io|
- # response.read_body do |chunk|
- # io.write chunk
+ # Net::HTTP.start(hostname) do |http|
+ # req = Net::HTTP::Get.new(uri)
+ # http.request(req) do |res|
+ # open('t.tmp', 'w') do |f|
+ # res.read_body do |chunk|
+ # f.write chunk
# end
# end
# end
# end
#
- # === HTTPS
- #
- # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=.
+ # == HTTPS
#
- # uri = URI('https://secure.example.com/some_path?query=string')
+ # HTTPS is enabled for an \HTTP connection by Net::HTTP#use_ssl=:
#
- # Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
- # request = Net::HTTP::Get.new uri
- # response = http.request request # Net::HTTPResponse object
+ # Net::HTTP.start(hostname, :use_ssl => true) do |http|
+ # req = Net::HTTP::Get.new(uri)
+ # res = http.request(req)
# end
#
- # Or if you simply want to make a GET request, you may pass in an URI
- # object that has an HTTPS URL. Net::HTTP automatically turns on TLS
- # verification if the URI object has a 'https' URI scheme.
+ # Or if you simply want to make a GET request, you may pass in a URI
+ # object that has an \HTTPS URL. \Net::HTTP automatically turns on TLS
+ # verification if the URI object has a 'https' URI scheme:
+ #
+ # uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Net::HTTP.get(uri)
+ #
+ # == Proxy Server
+ #
+ # An \HTTP object can have
+ # a {proxy server}[https://en.wikipedia.org/wiki/Proxy_server].
+ #
+ # You can create an \HTTP object with a proxy server
+ # using method Net::HTTP.new or method Net::HTTP.start.
+ #
+ # The proxy may be defined either by argument +p_addr+
+ # or by environment variable <tt>'http_proxy'</tt>.
+ #
+ # === Proxy Using Argument +p_addr+ as a \String
+ #
+ # When argument +p_addr+ is a string hostname,
+ # the returned +http+ has the given host as its proxy:
#
- # uri = URI('https://example.com/')
- # Net::HTTP.get(uri) # => String
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example')
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
#
- # In previous versions of Ruby you would need to require 'net/https' to use
- # HTTPS. This is no longer true.
+ # The port, username, and password for the proxy may also be given:
+ #
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Proxy Using '<tt>ENV['http_proxy']</tt>'
+ #
+ # When environment variable <tt>'http_proxy'</tt>
+ # is set to a \URI string,
+ # the returned +http+ will have the server at that URI as its proxy;
+ # note that the \URI string must have a protocol
+ # such as <tt>'http'</tt> or <tt>'https'</tt>:
+ #
+ # ENV['http_proxy'] = 'http://example.com'
+ # http = Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
+ #
+ # The \URI string may include proxy username, password, and port number:
+ #
+ # ENV['http_proxy'] = 'http://pname:ppass@example.com:8000'
+ # http = Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Filtering Proxies
+ #
+ # With method Net::HTTP.new (but not Net::HTTP.start),
+ # you can use argument +p_no_proxy+ to filter proxies:
+ #
+ # - Reject a certain address:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain domains or subdomains:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain addresses and port combinations:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:1234')
+ # http.proxy_address # => "proxy.example"
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # - Reject a list of the types above delimited using a comma:
+ #
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # == Compression and Decompression
+ #
+ # \Net::HTTP does not compress the body of a request before sending.
+ #
+ # By default, \Net::HTTP adds header <tt>'Accept-Encoding'</tt>
+ # to a new {request object}[rdoc-ref:Net::HTTPRequest]:
+ #
+ # Net::HTTP::Get.new(uri)['Accept-Encoding']
+ # # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ #
+ # This requests the server to zip-encode the response body if there is one;
+ # the server is not required to do so.
+ #
+ # \Net::HTTP does not automatically decompress a response body
+ # if the response has header <tt>'Content-Range'</tt>.
+ #
+ # Otherwise decompression (or not) depends on the value of header
+ # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-encoding-response-header]:
+ #
+ # - <tt>'deflate'</tt>, <tt>'gzip'</tt>, or <tt>'x-gzip'</tt>:
+ # decompresses the body and deletes the header.
+ # - <tt>'none'</tt> or <tt>'identity'</tt>:
+ # does not decompress the body, but deletes the header.
+ # - Any other value:
+ # leaves the body and header unchanged.
+ #
+ # == What's Here
+ #
+ # This is a categorized summary of methods and attributes.
+ #
+ # === \Net::HTTP Objects
+ #
+ # - {::new}[rdoc-ref:Net::HTTP.new]:
+ # Creates a new instance.
+ # - {#inspect}[rdoc-ref:Net::HTTP#inspect]:
+ # Returns a string representation of +self+.
+ #
+ # === Sessions
+ #
+ # - {::start}[rdoc-ref:Net::HTTP.start]:
+ # Begins a new session in a new \Net::HTTP object.
+ # - {#started?}[rdoc-ref:Net::HTTP#started?]
+ # (aliased as {#active?}[rdoc-ref:Net::HTTP#active?]):
+ # Returns whether in a session.
+ # - {#finish}[rdoc-ref:Net::HTTP#finish]:
+ # Ends an active session.
+ # - {#start}[rdoc-ref:Net::HTTP#start]:
+ # Begins a new session in an existing \Net::HTTP object (+self+).
+ #
+ # === Connections
+ #
+ # - {:continue_timeout}[rdoc-ref:Net::HTTP#continue_timeout]:
+ # Returns the continue timeout.
+ # - {#continue_timeout=}[rdoc-ref:Net::HTTP#continue_timeout=]:
+ # Sets the continue timeout seconds.
+ # - {:keep_alive_timeout}[rdoc-ref:Net::HTTP#keep_alive_timeout]:
+ # Returns the keep-alive timeout.
+ # - {:keep_alive_timeout=}[rdoc-ref:Net::HTTP#keep_alive_timeout=]:
+ # Sets the keep-alive timeout.
+ # - {:max_retries}[rdoc-ref:Net::HTTP#max_retries]:
+ # Returns the maximum retries.
+ # - {#max_retries=}[rdoc-ref:Net::HTTP#max_retries=]:
+ # Sets the maximum retries.
+ # - {:open_timeout}[rdoc-ref:Net::HTTP#open_timeout]:
+ # Returns the open timeout.
+ # - {:open_timeout=}[rdoc-ref:Net::HTTP#open_timeout=]:
+ # Sets the open timeout.
+ # - {:read_timeout}[rdoc-ref:Net::HTTP#read_timeout]:
+ # Returns the open timeout.
+ # - {:read_timeout=}[rdoc-ref:Net::HTTP#read_timeout=]:
+ # Sets the read timeout.
+ # - {:ssl_timeout}[rdoc-ref:Net::HTTP#ssl_timeout]:
+ # Returns the ssl timeout.
+ # - {:ssl_timeout=}[rdoc-ref:Net::HTTP#ssl_timeout=]:
+ # Sets the ssl timeout.
+ # - {:write_timeout}[rdoc-ref:Net::HTTP#write_timeout]:
+ # Returns the write timeout.
+ # - {write_timeout=}[rdoc-ref:Net::HTTP#write_timeout=]:
+ # Sets the write timeout.
+ #
+ # === Requests
+ #
+ # - {::get}[rdoc-ref:Net::HTTP.get]:
+ # Sends a GET request and returns the string response body.
+ # - {::get_print}[rdoc-ref:Net::HTTP.get_print]:
+ # Sends a GET request and write the string response body to $stdout.
+ # - {::get_response}[rdoc-ref:Net::HTTP.get_response]:
+ # Sends a GET request and returns a response object.
+ # - {::post_form}[rdoc-ref:Net::HTTP.post_form]:
+ # Sends a POST request with form data and returns a response object.
+ # - {::post}[rdoc-ref:Net::HTTP.post]:
+ # Sends a POST request with data and returns a response object.
+ # - {#copy}[rdoc-ref:Net::HTTP#copy]:
+ # Sends a COPY request and returns a response object.
+ # - {#delete}[rdoc-ref:Net::HTTP#delete]:
+ # Sends a DELETE request and returns a response object.
+ # - {#get}[rdoc-ref:Net::HTTP#get]:
+ # Sends a GET request and returns a response object.
+ # - {#head}[rdoc-ref:Net::HTTP#head]:
+ # Sends a HEAD request and returns a response object.
+ # - {#lock}[rdoc-ref:Net::HTTP#lock]:
+ # Sends a LOCK request and returns a response object.
+ # - {#mkcol}[rdoc-ref:Net::HTTP#mkcol]:
+ # Sends a MKCOL request and returns a response object.
+ # - {#move}[rdoc-ref:Net::HTTP#move]:
+ # Sends a MOVE request and returns a response object.
+ # - {#options}[rdoc-ref:Net::HTTP#options]:
+ # Sends a OPTIONS request and returns a response object.
+ # - {#patch}[rdoc-ref:Net::HTTP#patch]:
+ # Sends a PATCH request and returns a response object.
+ # - {#post}[rdoc-ref:Net::HTTP#post]:
+ # Sends a POST request and returns a response object.
+ # - {#propfind}[rdoc-ref:Net::HTTP#propfind]:
+ # Sends a PROPFIND request and returns a response object.
+ # - {#proppatch}[rdoc-ref:Net::HTTP#proppatch]:
+ # Sends a PROPPATCH request and returns a response object.
+ # - {#put}[rdoc-ref:Net::HTTP#put]:
+ # Sends a PUT request and returns a response object.
+ # - {#request}[rdoc-ref:Net::HTTP#request]:
+ # Sends a request and returns a response object.
+ # - {#request_get}[rdoc-ref:Net::HTTP#request_get]
+ # (aliased as {#get2}[rdoc-ref:Net::HTTP#get2]):
+ # Sends a GET request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_head}[rdoc-ref:Net::HTTP#request_head]
+ # (aliased as {#head2}[rdoc-ref:Net::HTTP#head2]):
+ # Sends a HEAD request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_post}[rdoc-ref:Net::HTTP#request_post]
+ # (aliased as {#post2}[rdoc-ref:Net::HTTP#post2]):
+ # Sends a POST request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#send_request}[rdoc-ref:Net::HTTP#send_request]:
+ # Sends a request and returns a response object.
+ # - {#trace}[rdoc-ref:Net::HTTP#trace]:
+ # Sends a TRACE request and returns a response object.
+ # - {#unlock}[rdoc-ref:Net::HTTP#unlock]:
+ # Sends an UNLOCK request and returns a response object.
+ #
+ # === Responses
+ #
+ # - {:close_on_empty_response}[rdoc-ref:Net::HTTP#close_on_empty_response]:
+ # Returns whether to close connection on empty response.
+ # - {:close_on_empty_response=}[rdoc-ref:Net::HTTP#close_on_empty_response=]:
+ # Sets whether to close connection on empty response.
+ # - {:ignore_eof}[rdoc-ref:Net::HTTP#ignore_eof]:
+ # Returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:ignore_eof=}[rdoc-ref:Net::HTTP#ignore_eof=]:
+ # Sets whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:response_body_encoding}[rdoc-ref:Net::HTTP#response_body_encoding]:
+ # Returns the encoding to use for the response body.
+ # - {#response_body_encoding=}[rdoc-ref:Net::HTTP#response_body_encoding=]:
+ # Sets the response body encoding.
#
# === Proxies
#
- # Net::HTTP will automatically create a proxy from the +http_proxy+
- # environment variable if it is present. To disable use of +http_proxy+,
- # pass +nil+ for the proxy address.
- #
- # You may also create a custom proxy:
- #
- # proxy_addr = 'your.proxy.host'
- # proxy_port = 8080
- #
- # Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
- # # always proxy via your.proxy.addr:8080
- # }
- #
- # See Net::HTTP.new for further details and examples such as proxies that
- # require a username and password.
- #
- # === Compression
- #
- # Net::HTTP automatically adds Accept-Encoding for compression of response
- # bodies and automatically decompresses gzip and deflate responses unless a
- # Range header was sent.
- #
- # Compression can be disabled through the Accept-Encoding: identity header.
- #
- # == HTTP Request Classes
- #
- # Here is the HTTP request class hierarchy.
- #
- # * Net::HTTPRequest
- # * Net::HTTP::Get
- # * Net::HTTP::Head
- # * Net::HTTP::Post
- # * Net::HTTP::Patch
- # * Net::HTTP::Put
- # * Net::HTTP::Proppatch
- # * Net::HTTP::Lock
- # * Net::HTTP::Unlock
- # * Net::HTTP::Options
- # * Net::HTTP::Propfind
- # * Net::HTTP::Delete
- # * Net::HTTP::Move
- # * Net::HTTP::Copy
- # * Net::HTTP::Mkcol
- # * Net::HTTP::Trace
- #
- # == HTTP Response Classes
- #
- # Here is HTTP response class hierarchy. All classes are defined in Net
- # module and are subclasses of Net::HTTPResponse.
- #
- # HTTPUnknownResponse:: For unhandled HTTP extensions
- # HTTPInformation:: 1xx
- # HTTPContinue:: 100
- # HTTPSwitchProtocol:: 101
- # HTTPProcessing:: 102
- # HTTPEarlyHints:: 103
- # HTTPSuccess:: 2xx
- # HTTPOK:: 200
- # HTTPCreated:: 201
- # HTTPAccepted:: 202
- # HTTPNonAuthoritativeInformation:: 203
- # HTTPNoContent:: 204
- # HTTPResetContent:: 205
- # HTTPPartialContent:: 206
- # HTTPMultiStatus:: 207
- # HTTPAlreadyReported:: 208
- # HTTPIMUsed:: 226
- # HTTPRedirection:: 3xx
- # HTTPMultipleChoices:: 300
- # HTTPMovedPermanently:: 301
- # HTTPFound:: 302
- # HTTPSeeOther:: 303
- # HTTPNotModified:: 304
- # HTTPUseProxy:: 305
- # HTTPTemporaryRedirect:: 307
- # HTTPPermanentRedirect:: 308
- # HTTPClientError:: 4xx
- # HTTPBadRequest:: 400
- # HTTPUnauthorized:: 401
- # HTTPPaymentRequired:: 402
- # HTTPForbidden:: 403
- # HTTPNotFound:: 404
- # HTTPMethodNotAllowed:: 405
- # HTTPNotAcceptable:: 406
- # HTTPProxyAuthenticationRequired:: 407
- # HTTPRequestTimeOut:: 408
- # HTTPConflict:: 409
- # HTTPGone:: 410
- # HTTPLengthRequired:: 411
- # HTTPPreconditionFailed:: 412
- # HTTPRequestEntityTooLarge:: 413
- # HTTPRequestURITooLong:: 414
- # HTTPUnsupportedMediaType:: 415
- # HTTPRequestedRangeNotSatisfiable:: 416
- # HTTPExpectationFailed:: 417
- # HTTPMisdirectedRequest:: 421
- # HTTPUnprocessableEntity:: 422
- # HTTPLocked:: 423
- # HTTPFailedDependency:: 424
- # HTTPUpgradeRequired:: 426
- # HTTPPreconditionRequired:: 428
- # HTTPTooManyRequests:: 429
- # HTTPRequestHeaderFieldsTooLarge:: 431
- # HTTPUnavailableForLegalReasons:: 451
- # HTTPServerError:: 5xx
- # HTTPInternalServerError:: 500
- # HTTPNotImplemented:: 501
- # HTTPBadGateway:: 502
- # HTTPServiceUnavailable:: 503
- # HTTPGatewayTimeOut:: 504
- # HTTPVersionNotSupported:: 505
- # HTTPVariantAlsoNegotiates:: 506
- # HTTPInsufficientStorage:: 507
- # HTTPLoopDetected:: 508
- # HTTPNotExtended:: 510
- # HTTPNetworkAuthenticationRequired:: 511
- #
- # There is also the Net::HTTPBadResponse exception which is raised when
- # there is a protocol error.
+ # - {:proxy_address}[rdoc-ref:Net::HTTP#proxy_address]:
+ # Returns the proxy address.
+ # - {:proxy_address=}[rdoc-ref:Net::HTTP#proxy_address=]:
+ # Sets the proxy address.
+ # - {::proxy_class?}[rdoc-ref:Net::HTTP.proxy_class?]:
+ # Returns whether +self+ is a proxy class.
+ # - {#proxy?}[rdoc-ref:Net::HTTP#proxy?]:
+ # Returns whether +self+ has a proxy.
+ # - {#proxy_address}[rdoc-ref:Net::HTTP#proxy_address]
+ # (aliased as {#proxyaddr}[rdoc-ref:Net::HTTP#proxyaddr]):
+ # Returns the proxy address.
+ # - {#proxy_from_env?}[rdoc-ref:Net::HTTP#proxy_from_env?]:
+ # Returns whether the proxy is taken from an environment variable.
+ # - {:proxy_from_env=}[rdoc-ref:Net::HTTP#proxy_from_env=]:
+ # Sets whether the proxy is to be taken from an environment variable.
+ # - {:proxy_pass}[rdoc-ref:Net::HTTP#proxy_pass]:
+ # Returns the proxy password.
+ # - {:proxy_pass=}[rdoc-ref:Net::HTTP#proxy_pass=]:
+ # Sets the proxy password.
+ # - {:proxy_port}[rdoc-ref:Net::HTTP#proxy_port]:
+ # Returns the proxy port.
+ # - {:proxy_port=}[rdoc-ref:Net::HTTP#proxy_port=]:
+ # Sets the proxy port.
+ # - {#proxy_user}[rdoc-ref:Net::HTTP#proxy_user]:
+ # Returns the proxy user name.
+ # - {:proxy_user=}[rdoc-ref:Net::HTTP#proxy_user=]:
+ # Sets the proxy user.
+ #
+ # === Security
+ #
+ # - {:ca_file}[rdoc-ref:Net::HTTP#ca_file]:
+ # Returns the path to a CA certification file.
+ # - {:ca_file=}[rdoc-ref:Net::HTTP#ca_file=]:
+ # Sets the path to a CA certification file.
+ # - {:ca_path}[rdoc-ref:Net::HTTP#ca_path]:
+ # Returns the path of to CA directory containing certification files.
+ # - {:ca_path=}[rdoc-ref:Net::HTTP#ca_path=]:
+ # Sets the path of to CA directory containing certification files.
+ # - {:cert}[rdoc-ref:Net::HTTP#cert]:
+ # Returns the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert=}[rdoc-ref:Net::HTTP#cert=]:
+ # Sets the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert_store}[rdoc-ref:Net::HTTP#cert_store]:
+ # Returns the X509::Store to be used for verifying peer certificate.
+ # - {:cert_store=}[rdoc-ref:Net::HTTP#cert_store=]:
+ # Sets the X509::Store to be used for verifying peer certificate.
+ # - {:ciphers}[rdoc-ref:Net::HTTP#ciphers]:
+ # Returns the available SSL ciphers.
+ # - {:ciphers=}[rdoc-ref:Net::HTTP#ciphers=]:
+ # Sets the available SSL ciphers.
+ # - {:extra_chain_cert}[rdoc-ref:Net::HTTP#extra_chain_cert]:
+ # Returns the extra X509 certificates to be added to the certificate chain.
+ # - {:extra_chain_cert=}[rdoc-ref:Net::HTTP#extra_chain_cert=]:
+ # Sets the extra X509 certificates to be added to the certificate chain.
+ # - {:key}[rdoc-ref:Net::HTTP#key]:
+ # Returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:key=}[rdoc-ref:Net::HTTP#key=]:
+ # Sets the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:max_version}[rdoc-ref:Net::HTTP#max_version]:
+ # Returns the maximum SSL version.
+ # - {:max_version=}[rdoc-ref:Net::HTTP#max_version=]:
+ # Sets the maximum SSL version.
+ # - {:min_version}[rdoc-ref:Net::HTTP#min_version]:
+ # Returns the minimum SSL version.
+ # - {:min_version=}[rdoc-ref:Net::HTTP#min_version=]:
+ # Sets the minimum SSL version.
+ # - {#peer_cert}[rdoc-ref:Net::HTTP#peer_cert]:
+ # Returns the X509 certificate chain for the session's socket peer.
+ # - {:ssl_version}[rdoc-ref:Net::HTTP#ssl_version]:
+ # Returns the SSL version.
+ # - {:ssl_version=}[rdoc-ref:Net::HTTP#ssl_version=]:
+ # Sets the SSL version.
+ # - {#use_ssl=}[rdoc-ref:Net::HTTP#use_ssl=]:
+ # Sets whether a new session is to use Transport Layer Security.
+ # - {#use_ssl?}[rdoc-ref:Net::HTTP#use_ssl?]:
+ # Returns whether +self+ uses SSL.
+ # - {:verify_callback}[rdoc-ref:Net::HTTP#verify_callback]:
+ # Returns the callback for the server certification verification.
+ # - {:verify_callback=}[rdoc-ref:Net::HTTP#verify_callback=]:
+ # Sets the callback for the server certification verification.
+ # - {:verify_depth}[rdoc-ref:Net::HTTP#verify_depth]:
+ # Returns the maximum depth for the certificate chain verification.
+ # - {:verify_depth=}[rdoc-ref:Net::HTTP#verify_depth=]:
+ # Sets the maximum depth for the certificate chain verification.
+ # - {:verify_hostname}[rdoc-ref:Net::HTTP#verify_hostname]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_hostname=}[rdoc-ref:Net::HTTP#verify_hostname=]:
+ # Sets he flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode}[rdoc-ref:Net::HTTP#verify_mode]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode=}[rdoc-ref:Net::HTTP#verify_mode=]:
+ # Sets the flags for server the certification verification at the beginning of the SSL/TLS session.
+ #
+ # === Addresses and Ports
+ #
+ # - {:address}[rdoc-ref:Net::HTTP#address]:
+ # Returns the string host name or host IP.
+ # - {::default_port}[rdoc-ref:Net::HTTP.default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::http_default_port}[rdoc-ref:Net::HTTP.http_default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::https_default_port}[rdoc-ref:Net::HTTP.https_default_port]:
+ # Returns integer 443, the default port to use for HTTPS requests.
+ # - {#ipaddr}[rdoc-ref:Net::HTTP#ipaddr]:
+ # Returns the IP address for the connection.
+ # - {#ipaddr=}[rdoc-ref:Net::HTTP#ipaddr=]:
+ # Sets the IP address for the connection.
+ # - {:local_host}[rdoc-ref:Net::HTTP#local_host]:
+ # Returns the string local host used to establish the connection.
+ # - {:local_host=}[rdoc-ref:Net::HTTP#local_host=]:
+ # Sets the string local host used to establish the connection.
+ # - {:local_port}[rdoc-ref:Net::HTTP#local_port]:
+ # Returns the integer local port used to establish the connection.
+ # - {:local_port=}[rdoc-ref:Net::HTTP#local_port=]:
+ # Sets the integer local port used to establish the connection.
+ # - {:port}[rdoc-ref:Net::HTTP#port]:
+ # Returns the integer port number.
+ #
+ # === \HTTP Version
+ #
+ # - {::version_1_2?}[rdoc-ref:Net::HTTP.version_1_2?]
+ # (aliased as {::is_version_1_2?}[rdoc-ref:Net::HTTP.is_version_1_2?]
+ # and {::version_1_2}[rdoc-ref:Net::HTTP.version_1_2]):
+ # Returns true; retained for compatibility.
+ #
+ # === Debugging
+ #
+ # - {#set_debug_output}[rdoc-ref:Net::HTTP#set_debug_output]:
+ # Sets the output stream for debugging.
#
class HTTP < Protocol
# :stopdoc:
- VERSION = "0.2.2"
- Revision = %q$Revision$.split[1]
+ VERSION = "0.4.1"
HTTPVersion = '1.1'
begin
require 'zlib'
@@ -408,18 +732,17 @@ module Net #:nodoc:
end
# :startdoc:
- # Turns on net/http 1.2 (Ruby 1.8) features.
- # Defaults to ON in Ruby 1.8 or later.
+ # Returns +true+; retained for compatibility.
def HTTP.version_1_2
true
end
- # Returns true if net/http is in version 1.2 mode.
- # Defaults to true.
+ # Returns +true+; retained for compatibility.
def HTTP.version_1_2?
true
end
+ # Returns +false+; retained for compatibility.
def HTTP.version_1_1? #:nodoc:
false
end
@@ -429,25 +752,12 @@ module Net #:nodoc:
alias is_version_1_2? version_1_2? #:nodoc:
end
+ # :call-seq:
+ # Net::HTTP.get_print(hostname, path, port = 80) -> nil
+ # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil
#
- # short cut methods
- #
-
- #
- # Gets the body text from the target and outputs it to $stdout. The
- # target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
- #
- # Net::HTTP.get_print URI('http://www.example.com/index.html')
- #
- # or:
- #
- # Net::HTTP.get_print 'www.example.com', '/index.html'
- #
- # you can also specify request headers:
- #
- # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }
- #
+ # Like Net::HTTP.get, but writes the returned body to $stdout;
+ # returns +nil+.
def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
get_response(uri_or_host, path_or_headers, port) {|res|
res.read_body do |chunk|
@@ -457,40 +767,48 @@ module Net #:nodoc:
nil
end
- # Sends a GET request to the target and returns the HTTP response
- # as a string. The target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
- #
- # print Net::HTTP.get(URI('http://www.example.com/index.html'))
+ # :call-seq:
+ # Net::HTTP.get(hostname, path, port = 80) -> body
+ # Net::HTTP:get(uri, headers = {}, port = uri.port) -> body
#
- # or:
+ # Sends a GET request and returns the \HTTP response body as a string.
#
- # print Net::HTTP.get('www.example.com', '/index.html')
+ # With string arguments +hostname+ and +path+:
#
- # you can also specify request headers:
+ # hostname = 'jsonplaceholder.typicode.com'
+ # path = '/todos/1'
+ # puts Net::HTTP.get(hostname, path)
#
- # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
+ # Output:
#
- def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
- get_response(uri_or_host, path_or_headers, port).body
- end
-
- # Sends a GET request to the target and returns the HTTP response
- # as a Net::HTTPResponse object. The target can either be specified as
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
#
- # res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
- # print res.body
+ # With URI object +uri+ and optional hash argument +headers+:
#
- # or:
+ # uri = URI('https://jsonplaceholder.typicode.com/todos/1')
+ # headers = {'Content-type' => 'application/json; charset=UTF-8'}
+ # Net::HTTP.get(uri, headers)
#
- # res = Net::HTTP.get_response('www.example.com', '/index.html')
- # print res.body
+ # Related:
#
- # you can also specify request headers:
+ # - Net::HTTP::Get: request class for \HTTP method +GET+.
+ # - Net::HTTP#get: convenience method for \HTTP method +GET+.
#
- # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
+ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
+ get_response(uri_or_host, path_or_headers, port).body
+ end
+
+ # :call-seq:
+ # Net::HTTP.get_response(hostname, path, port = 80) -> http_response
+ # Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response
#
+ # Like Net::HTTP.get, but returns a Net::HTTPResponse object
+ # instead of the body string.
def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
if path_or_headers && !path_or_headers.is_a?(Hash)
host = uri_or_host
@@ -508,16 +826,31 @@ module Net #:nodoc:
end
end
- # Posts data to the specified URI object.
+ # Posts data to a host; returns a Net::HTTPResponse object.
#
- # Example:
+ # Argument +url+ must be a URL;
+ # argument +data+ must be a string:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # headers = {'content-type': 'application/json'}
+ # res = Net::HTTP.post(_uri, data, headers) # => #<Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
+ #
+ # Output:
#
- # require 'net/http'
- # require 'uri'
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": 1,
+ # "id": 101
+ # }
#
- # Net::HTTP.post URI('http://www.example.com/api/search'),
- # { "q" => "ruby", "max" => "50" }.to_json,
- # "Content-Type" => "application/json"
+ # Related:
+ #
+ # - Net::HTTP::Post: request class for \HTTP method +POST+.
+ # - Net::HTTP#post: convenience method for \HTTP method +POST+.
#
def HTTP.post(url, data, header = nil)
start(url.hostname, url.port,
@@ -526,22 +859,25 @@ module Net #:nodoc:
}
end
- # Posts HTML form data to the specified URI object.
- # The form data must be provided as a Hash mapping from String to String.
- # Example:
+ # Posts data to a host; returns a Net::HTTPResponse object.
#
- # { "cmd" => "search", "q" => "ruby", "max" => "50" }
+ # Argument +url+ must be a URI;
+ # argument +data+ must be a hash:
#
- # This method also does Basic Authentication if and only if +url+.user exists.
- # But userinfo for authentication is deprecated (RFC3986).
- # So this feature will be removed.
- #
- # Example:
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = {title: 'foo', body: 'bar', userId: 1}
+ # res = Net::HTTP.post_form(_uri, data) # => #<Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
#
- # require 'net/http'
+ # Output:
#
- # Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
- # { "q" => "ruby", "max" => "50" }
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": "1",
+ # "id": 101
+ # }
#
def HTTP.post_form(url, params)
req = Post.new(url)
@@ -554,20 +890,29 @@ module Net #:nodoc:
end
#
- # HTTP session management
+ # \HTTP session management
#
- # The default port to use for HTTP requests; defaults to 80.
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Net::HTTP.default_port # => 80
+ #
def HTTP.default_port
http_default_port()
end
- # The default port to use for HTTP requests; defaults to 80.
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Net::HTTP.http_default_port # => 80
+ #
def HTTP.http_default_port
80
end
- # The default port to use for HTTPS requests; defaults to 443.
+ # Returns integer +443+, the default port to use for HTTPS requests:
+ #
+ # Net::HTTP.https_default_port # => 443
+ #
def HTTP.https_default_port
443
end
@@ -577,35 +922,91 @@ module Net #:nodoc:
end
# :call-seq:
- # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
- # HTTP.start(address, port=nil, p_addr=:ENV, p_port=nil, p_user=nil, p_pass=nil, opt, &block)
- #
- # Creates a new Net::HTTP object, then additionally opens the TCP
- # connection and HTTP session.
- #
- # Arguments are the following:
- # _address_ :: hostname or IP address of the server
- # _port_ :: port of the server
- # _p_addr_ :: address of proxy
- # _p_port_ :: port of proxy
- # _p_user_ :: user of proxy
- # _p_pass_ :: pass of proxy
- # _opt_ :: optional hash
- #
- # _opt_ sets following values by its accessor.
- # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers, keep_alive_timeout,
- # close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
- # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
- # If you set :use_ssl as true, you can use https and default value of
- # verify_mode is set as OpenSSL::SSL::VERIFY_PEER.
- #
- # If the optional block is given, the newly
- # created Net::HTTP object is passed to it and closed when the
- # block finishes. In this case, the return value of this method
- # is the return value of the block. If no block is given, the
- # return value of this method is the newly created Net::HTTP object
- # itself, and the caller is responsible for closing it upon completion
- # using the finish() method.
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) -> http
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) {|http| ... } -> object
+ #
+ # Creates a new \Net::HTTP object, +http+, via \Net::HTTP.new:
+ #
+ # - For arguments +address+ and +port+, see Net::HTTP.new.
+ # - For proxy-defining arguments +p_addr+ through +p_pass+,
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
+ # - For argument +opts+, see below.
+ #
+ # With no block given:
+ #
+ # - Calls <tt>http.start</tt> with no block (see #start),
+ # which opens a TCP connection and \HTTP session.
+ # - Returns +http+.
+ # - The caller should call #finish to close the session:
+ #
+ # http = Net::HTTP.start(hostname)
+ # http.started? # => true
+ # http.finish
+ # http.started? # => false
+ #
+ # With a block given:
+ #
+ # - Calls <tt>http.start</tt> with the block (see #start), which:
+ #
+ # - Opens a TCP connection and \HTTP session.
+ # - Calls the block,
+ # which may make any number of requests to the host.
+ # - Closes the \HTTP session and TCP connection on block exit.
+ # - Returns the block's value +object+.
+ #
+ # - Returns +object+.
+ #
+ # Example:
+ #
+ # hostname = 'jsonplaceholder.typicode.com'
+ # Net::HTTP.start(hostname) do |http|
+ # puts http.get('/todos/1').body
+ # puts http.get('/todos/2').body
+ # end
+ #
+ # Output:
+ #
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
+ # {
+ # "userId": 1,
+ # "id": 2,
+ # "title": "quis ut nam facilis et officia qui",
+ # "completed": false
+ # }
+ #
+ # If the last argument given is a hash, it is the +opts+ hash,
+ # where each key is a method or accessor to be called,
+ # and its value is the value to be set.
+ #
+ # The keys may include:
+ #
+ # - #ca_file
+ # - #ca_path
+ # - #cert
+ # - #cert_store
+ # - #ciphers
+ # - #close_on_empty_response
+ # - +ipaddr+ (calls #ipaddr=)
+ # - #keep_alive_timeout
+ # - #key
+ # - #open_timeout
+ # - #read_timeout
+ # - #ssl_timeout
+ # - #ssl_version
+ # - +use_ssl+ (calls #use_ssl=)
+ # - #verify_callback
+ # - #verify_depth
+ # - #verify_mode
+ # - #write_timeout
+ #
+ # Note: If +port+ is +nil+ and <tt>opts[:use_ssl]</tt> is a truthy value,
+ # the value passed to +new+ is Net::HTTP.https_default_port, not +port+.
+ #
def HTTP.start(address, *arg, &block) # :yield: +http+
arg.pop if opt = Hash.try_convert(arg[-1])
port, p_addr, p_port, p_user, p_pass = *arg
@@ -632,25 +1033,34 @@ module Net #:nodoc:
alias newobj new # :nodoc:
end
- # Creates a new Net::HTTP object without opening a TCP connection or
- # HTTP session.
+ # Returns a new \Net::HTTP object +http+
+ # (but does not open a TCP connection or \HTTP session).
+ #
+ # With only string argument +address+ given
+ # (and <tt>ENV['http_proxy']</tt> undefined or +nil+),
+ # the returned +http+:
+ #
+ # - Has the given address.
+ # - Has the default port number, Net::HTTP.default_port (80).
+ # - Has no proxy.
#
- # The +address+ should be a DNS hostname or IP address, the +port+ is the
- # port the server operates on. If no +port+ is given the default port for
- # HTTP or HTTPS is used.
+ # Example:
+ #
+ # http = Net::HTTP.new(hostname)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.address # => "jsonplaceholder.typicode.com"
+ # http.port # => 80
+ # http.proxy? # => false
+ #
+ # With integer argument +port+ also given,
+ # the returned +http+ has the given port:
#
- # If none of the +p_+ arguments are given, the proxy host and port are
- # taken from the +http_proxy+ environment variable (or its uppercase
- # equivalent) if present. If the proxy requires authentication you must
- # supply it by hand. See URI::Generic#find_proxy for details of proxy
- # detection from the environment. To disable proxy detection set +p_addr+
- # to nil.
+ # http = Net::HTTP.new(hostname, 8000)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:8000 open=false>
+ # http.port # => 8000
#
- # If you are connecting to a custom proxy, +p_addr+ specifies the DNS name
- # or IP address of the proxy host, +p_port+ the port to use to access the
- # proxy, +p_user+ and +p_pass+ the username and password if authorization
- # is required to use the proxy, and p_no_proxy hosts which do not
- # use the proxy.
+ # For proxy-defining arguments +p_addr+ through +p_no_proxy+,
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
#
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
http = super address, port
@@ -664,7 +1074,7 @@ module Net #:nodoc:
elsif p_addr == :ENV then
http.proxy_from_env = true
else
- if p_addr && p_no_proxy && !URI::Generic.use_proxy?(p_addr, p_addr, p_port, p_no_proxy)
+ if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port, p_no_proxy)
p_addr = nil
p_port = nil
end
@@ -677,10 +1087,10 @@ module Net #:nodoc:
http
end
- # Creates a new Net::HTTP object for the specified server address,
- # without opening the TCP connection or initializing the HTTP session.
+ # Creates a new \Net::HTTP object for the specified server address,
+ # without opening the TCP connection or initializing the \HTTP session.
# The +address+ should be a DNS hostname or IP address.
- def initialize(address, port = nil)
+ def initialize(address, port = nil) # :nodoc:
@address = address
@port = (port || HTTP.default_port)
@ipaddr = nil
@@ -717,6 +1127,11 @@ module Net #:nodoc:
end
end
+ # Returns a string representation of +self+:
+ #
+ # Net::HTTP.new(hostname).inspect
+ # # => "#<Net::HTTP jsonplaceholder.typicode.com:80 open=false>"
+ #
def inspect
"#<#{self.class} #{@address}:#{@port} open=#{started?}>"
end
@@ -724,83 +1139,184 @@ module Net #:nodoc:
# *WARNING* This method opens a serious security hole.
# Never use this method in production code.
#
- # Sets an output stream for debugging.
+ # Sets the output stream for debugging:
#
# http = Net::HTTP.new(hostname)
- # http.set_debug_output $stderr
- # http.start { .... }
+ # File.open('t.tmp', 'w') do |file|
+ # http.set_debug_output(file)
+ # http.start
+ # http.get('/nosuch/1')
+ # http.finish
+ # end
+ # puts File.read('t.tmp')
+ #
+ # Output:
+ #
+ # opening connection to jsonplaceholder.typicode.com:80...
+ # opened
+ # <- "GET /nosuch/1 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nHost: jsonplaceholder.typicode.com\r\n\r\n"
+ # -> "HTTP/1.1 404 Not Found\r\n"
+ # -> "Date: Mon, 12 Dec 2022 21:14:11 GMT\r\n"
+ # -> "Content-Type: application/json; charset=utf-8\r\n"
+ # -> "Content-Length: 2\r\n"
+ # -> "Connection: keep-alive\r\n"
+ # -> "X-Powered-By: Express\r\n"
+ # -> "X-Ratelimit-Limit: 1000\r\n"
+ # -> "X-Ratelimit-Remaining: 999\r\n"
+ # -> "X-Ratelimit-Reset: 1670879660\r\n"
+ # -> "Vary: Origin, Accept-Encoding\r\n"
+ # -> "Access-Control-Allow-Credentials: true\r\n"
+ # -> "Cache-Control: max-age=43200\r\n"
+ # -> "Pragma: no-cache\r\n"
+ # -> "Expires: -1\r\n"
+ # -> "X-Content-Type-Options: nosniff\r\n"
+ # -> "Etag: W/\"2-vyGp6PvFo4RvsFtPoIWeCReyIC8\"\r\n"
+ # -> "Via: 1.1 vegur\r\n"
+ # -> "CF-Cache-Status: MISS\r\n"
+ # -> "Server-Timing: cf-q-config;dur=1.3000000762986e-05\r\n"
+ # -> "Report-To: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=yOr40jo%2BwS1KHzhTlVpl54beJ5Wx2FcG4gGV0XVrh3X9OlR5q4drUn2dkt5DGO4GDcE%2BVXT7CNgJvGs%2BZleIyMu8CLieFiDIvOviOY3EhHg94m0ZNZgrEdpKD0S85S507l1vsEwEHkoTm%2Ff19SiO\"}],\"group\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "NEL: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "Server: cloudflare\r\n"
+ # -> "CF-RAY: 778977dc484ce591-DFW\r\n"
+ # -> "alt-svc: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\r\n"
+ # -> "\r\n"
+ # reading 2 bytes...
+ # -> "{}"
+ # read 2 bytes
+ # Conn keep-alive
#
def set_debug_output(output)
warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
@debug_output = output
end
- # The DNS host name or IP address to connect to.
+ # Returns the string host name or host IP given as argument +address+ in ::new.
attr_reader :address
- # The port number to connect to.
+ # Returns the integer port number given as argument +port+ in ::new.
attr_reader :port
- # The local host used to establish the connection.
+ # Sets or returns the string local host used to establish the connection;
+ # initially +nil+.
attr_accessor :local_host
- # The local port used to establish the connection.
+ # Sets or returns the integer local port used to establish the connection;
+ # initially +nil+.
attr_accessor :local_port
- # The encoding to use for the response body. If Encoding, uses the
- # specified encoding. If other true value, tries to detect the response
- # body encoding.
+ # Returns the encoding to use for the response body;
+ # see #response_body_encoding=.
attr_reader :response_body_encoding
- # Set the encoding to use for the response body. If given a String, find
- # the related Encoding.
+ # Sets the encoding to be used for the response body;
+ # returns the encoding.
+ #
+ # The given +value+ may be:
+ #
+ # - An Encoding object.
+ # - The name of an encoding.
+ # - An alias for an encoding name.
+ #
+ # See {Encoding}[rdoc-ref:Encoding].
+ #
+ # Examples:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.response_body_encoding = Encoding::US_ASCII # => #<Encoding:US-ASCII>
+ # http.response_body_encoding = 'US-ASCII' # => "US-ASCII"
+ # http.response_body_encoding = 'ASCII' # => "ASCII"
+ #
def response_body_encoding=(value)
value = Encoding.find(value) if value.is_a?(String)
@response_body_encoding = value
end
+ # Sets whether to determine the proxy from environment variable
+ # '<tt>ENV['http_proxy']</tt>';
+ # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27].
attr_writer :proxy_from_env
+
+ # Sets the proxy address;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_address
+
+ # Sets the proxy port;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_port
+
+ # Sets the proxy user;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_user
+
+ # Sets the proxy password;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_pass
- # The IP address to connect to/used to connect to
+ # Returns the IP address for the connection.
+ #
+ # If the session has not been started,
+ # returns the value set by #ipaddr=,
+ # or +nil+ if it has not been set:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # If the session has been started,
+ # returns the IP address from the socket:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.ipaddr # => "172.67.155.76"
+ # http.finish
+ #
def ipaddr
started? ? @socket.io.peeraddr[3] : @ipaddr
end
- # Set the IP address to connect to
+ # Sets the IP address for the connection:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # The IP address may not be set if the session has been started.
def ipaddr=(addr)
raise IOError, "ipaddr value changed, but session already started" if started?
@ipaddr = addr
end
- # Number of seconds to wait for the connection to open. Any number
- # may be used, including Floats for fractional seconds. If the HTTP
- # object cannot open a connection in this many seconds, it raises a
- # Net::OpenTimeout exception. The default value is 60 seconds.
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to wait for a connection to open;
+ # initially 60.
+ # If the connection is not made in the given interval,
+ # an exception is raised.
attr_accessor :open_timeout
- # Number of seconds to wait for one block to be read (via one read(2)
- # call). Any number may be used, including Floats for fractional
- # seconds. If the HTTP object cannot read data in this many seconds,
- # it raises a Net::ReadTimeout exception. The default value is 60 seconds.
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be read (via one read(2) call);
+ # see #read_timeout=.
attr_reader :read_timeout
- # Number of seconds to wait for one block to be written (via one write(2)
- # call). Any number may be used, including Floats for fractional
- # seconds. If the HTTP object cannot write data in this many seconds,
- # it raises a Net::WriteTimeout exception. The default value is 60 seconds.
- # Net::WriteTimeout is not raised on Windows.
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be written (via one write(2) call);
+ # see #write_timeout=.
attr_reader :write_timeout
- # Maximum number of times to retry an idempotent request in case of
- # Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
+ # Sets the maximum number of times to retry an idempotent request in case of
+ # \Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
# Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError,
# Timeout::Error.
- # Should be a non-negative integer number. Zero means no retries.
- # The default value is 1.
+ # The initial value is 1.
+ #
+ # Argument +retries+ must be a non-negative numeric value:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.max_retries = 2 # => 2
+ # http.max_retries # => 2
+ #
def max_retries=(retries)
retries = retries.to_int
if retries < 0
@@ -809,59 +1325,113 @@ module Net #:nodoc:
@max_retries = retries
end
+ # Returns the maximum number of times to retry an idempotent request;
+ # see #max_retries=.
attr_reader :max_retries
- # Setter for the read_timeout attribute.
+ # Sets the read timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.read_timeout # => 60
+ # http.get('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
+ # http.read_timeout = 0
+ # http.get('/todos/1') # Raises Net::ReadTimeout.
+ #
def read_timeout=(sec)
@socket.read_timeout = sec if @socket
@read_timeout = sec
end
- # Setter for the write_timeout attribute.
+ # Sets the write timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # body = 'bar' * 200000
+ # data = <<EOF
+ # {"title": "foo", "body": "#{body}", "userId": "1"}
+ # EOF
+ # headers = {'content-type': 'application/json'}
+ # http = Net::HTTP.new(hostname)
+ # http.write_timeout # => 60
+ # http.post(_uri.path, data, headers)
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
+ # http.write_timeout = 0
+ # http.post(_uri.path, data, headers) # Raises Net::WriteTimeout.
+ #
def write_timeout=(sec)
@socket.write_timeout = sec if @socket
@write_timeout = sec
end
- # Seconds to wait for 100 Continue response. If the HTTP object does not
- # receive a response in this many seconds it sends the request body. The
- # default value is +nil+.
+ # Returns the continue timeout value;
+ # see continue_timeout=.
attr_reader :continue_timeout
- # Setter for the continue_timeout attribute.
+ # Sets the continue timeout value,
+ # which is the number of seconds to wait for an expected 100 Continue response.
+ # If the \HTTP object does not receive a response in this many seconds
+ # it sends the request body.
def continue_timeout=(sec)
@socket.continue_timeout = sec if @socket
@continue_timeout = sec
end
- # Seconds to reuse the connection of the previous request.
- # If the idle time is less than this Keep-Alive Timeout,
- # Net::HTTP reuses the TCP/IP socket used by the previous communication.
- # The default value is 2 seconds.
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to keep the connection open after a request is sent;
+ # initially 2.
+ # If a new request is made during the given interval,
+ # the still-open connection is used;
+ # otherwise the connection will have been closed
+ # and a new connection is opened.
attr_accessor :keep_alive_timeout
- # Whether to ignore EOF when reading response bodies with defined
- # Content-Length headers. For backwards compatibility, the default is true.
+ # Sets or returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers;
+ # initially +true+.
attr_accessor :ignore_eof
- # Returns true if the HTTP session has been started.
+ # Returns +true+ if the \HTTP session has been started:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.started? # => false
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Net::HTTP.start(hostname) do |http|
+ # http.started?
+ # end # => true
+ # http.started? # => false
+ #
def started?
@started
end
alias active? started? #:nodoc: obsolete
+ # Sets or returns whether to close the connection when the response is empty;
+ # initially +false+.
attr_accessor :close_on_empty_response
- # Returns true if SSL/TLS is being used with HTTP.
+ # Returns +true+ if +self+ uses SSL, +false+ otherwise.
+ # See Net::HTTP#use_ssl=.
def use_ssl?
@use_ssl
end
- # Turn on/off SSL.
- # This flag must be set before starting session.
- # If you change use_ssl value after session started,
- # a Net::HTTP object raises IOError.
+ # Sets whether a new session is to use
+ # {Transport Layer Security}[https://en.wikipedia.org/wiki/Transport_Layer_Security]:
+ #
+ # Raises IOError if attempting to change during a session.
+ #
+ # Raises OpenSSL::SSL::SSLError if the port is not an HTTPS port.
def use_ssl=(flag)
flag = flag ? true : false
if started? and @use_ssl != flag
@@ -886,7 +1456,7 @@ module Net #:nodoc:
:@verify_depth,
:@verify_mode,
:@verify_hostname,
- ]
+ ] # :nodoc:
SSL_ATTRIBUTES = [
:ca_file,
:ca_path,
@@ -903,64 +1473,67 @@ module Net #:nodoc:
:verify_depth,
:verify_mode,
:verify_hostname,
- ]
+ ] # :nodoc:
- # Sets path of a CA certification file in PEM format.
- #
- # The file can contain several CA certificates.
+ # Sets or returns the path to a CA certification file in PEM format.
attr_accessor :ca_file
- # Sets path of a CA certification directory containing certifications in
- # PEM format.
+ # Sets or returns the path of to CA directory
+ # containing certification files in PEM format.
attr_accessor :ca_path
- # Sets an OpenSSL::X509::Certificate object as client certificate.
- # (This method is appeared in Michal Rokos's OpenSSL extension).
+ # Sets or returns the OpenSSL::X509::Certificate object
+ # to be used for client certification.
attr_accessor :cert
- # Sets the X509::Store to verify peer certificate.
+ # Sets or returns the X509::Store to be used for verifying peer certificate.
attr_accessor :cert_store
- # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers=
+ # Sets or returns the available SSL ciphers.
+ # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers-3D].
attr_accessor :ciphers
- # Sets the extra X509 certificates to be added to the certificate chain.
- # See OpenSSL::SSL::SSLContext#extra_chain_cert=
+ # Sets or returns the extra X509 certificates to be added to the certificate chain.
+ # See {OpenSSL::SSL::SSLContext#add_certificate}[rdoc-ref:OpenSSL::SSL::SSLContext#add_certificate].
attr_accessor :extra_chain_cert
- # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
- # (This method is appeared in Michal Rokos's OpenSSL extension.)
+ # Sets or returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
attr_accessor :key
- # Sets the SSL timeout seconds.
+ # Sets or returns the SSL timeout seconds.
attr_accessor :ssl_timeout
- # Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version=
+ # Sets or returns the SSL version.
+ # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D].
attr_accessor :ssl_version
- # Sets the minimum SSL version. See OpenSSL::SSL::SSLContext#min_version=
+ # Sets or returns the minimum SSL version.
+ # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D].
attr_accessor :min_version
- # Sets the maximum SSL version. See OpenSSL::SSL::SSLContext#max_version=
+ # Sets or returns the maximum SSL version.
+ # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D].
attr_accessor :max_version
- # Sets the verify callback for the server certification verification.
+ # Sets or returns the callback for the server certification verification.
attr_accessor :verify_callback
- # Sets the maximum depth for the certificate chain verification.
+ # Sets or returns the maximum depth for the certificate chain verification.
attr_accessor :verify_depth
- # Sets the flags for server the certification verification at beginning of
- # SSL/TLS session.
- #
+ # Sets or returns the flags for server the certification verification
+ # at the beginning of the SSL/TLS session.
# OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
attr_accessor :verify_mode
- # Sets to check the server certificate is valid for the hostname.
- # See OpenSSL::SSL::SSLContext#verify_hostname=
+ # Sets or returns whether to verify that the server certificate is valid
+ # for the hostname.
+ # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_mode].
attr_accessor :verify_hostname
- # Returns the X.509 certificates the server presented.
+ # Returns the X509 certificate chain (an array of strings)
+ # for the session's socket peer,
+ # or +nil+ if none.
def peer_cert
if not use_ssl? or not @socket
return nil
@@ -968,14 +1541,26 @@ module Net #:nodoc:
@socket.io.peer_cert
end
- # Opens a TCP connection and HTTP session.
+ # Starts an \HTTP session.
#
- # When this method is called with a block, it passes the Net::HTTP
- # object to the block, and closes the TCP connection and HTTP session
- # after the block has been executed.
+ # Without a block, returns +self+:
#
- # When called with a block, it returns the return value of the
- # block; otherwise, it returns self.
+ # http = Net::HTTP.new(hostname)
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.start
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=true>
+ # http.started? # => true
+ # http.finish
+ #
+ # With a block, calls the block with +self+,
+ # finishes the session when the block exits,
+ # and returns the block's value:
+ #
+ # http.start do |http|
+ # http
+ # end
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.started? # => false
#
def start # :yield: http
raise IOError, 'HTTP session already opened' if @started
@@ -1013,13 +1598,14 @@ module Net #:nodoc:
end
debug "opening connection to #{conn_addr}:#{conn_port}..."
- begin
- s = Socket.tcp conn_addr, conn_port, @local_host, @local_port, connect_timeout: @open_timeout
- rescue => e
- e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) #for compatibility with previous versions
- raise e, "Failed to open TCP connection to " +
- "#{conn_addr}:#{conn_port} (#{e.message})"
- end
+ s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
+ begin
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
+ rescue => e
+ raise e, "Failed to open TCP connection to " +
+ "#{conn_addr}:#{conn_port} (#{e.message})"
+ end
+ }
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
debug "opened"
if use_ssl?
@@ -1028,8 +1614,8 @@ module Net #:nodoc:
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
- buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n"
- buf << "Host: #{@address}:#{@port}\r\n"
+ buf = +"CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" \
+ "Host: #{@address}:#{@port}\r\n"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
buf << "Proxy-Authorization: Basic #{credential}\r\n"
@@ -1110,8 +1696,15 @@ module Net #:nodoc:
end
private :on_connect
- # Finishes the HTTP session and closes the TCP connection.
- # Raises IOError if the session has not been started.
+ # Finishes the \HTTP session:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Raises IOError if not in a session.
def finish
raise IOError, 'HTTP session not yet started' unless started?
do_finish
@@ -1138,12 +1731,12 @@ module Net #:nodoc:
@proxy_user = nil
@proxy_pass = nil
- # Creates an HTTP proxy class which behaves like Net::HTTP, but
+ # Creates an \HTTP proxy class which behaves like \Net::HTTP, but
# performs all access via the specified proxy.
#
# This class is obsolete. You may pass these same parameters directly to
- # Net::HTTP.new. See Net::HTTP.new for details of the arguments.
- def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
+ # \Net::HTTP.new. See Net::HTTP.new for details of the arguments.
+ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
return self unless p_addr
Class.new(self) {
@@ -1165,31 +1758,37 @@ module Net #:nodoc:
end
class << HTTP
- # returns true if self is a class which was created by HTTP::Proxy.
+ # Returns true if self is a class which was created by HTTP::Proxy.
def proxy_class?
defined?(@is_proxy_class) ? @is_proxy_class : false
end
- # Address of proxy host. If Net::HTTP does not use a proxy, nil.
+ # Returns the address of the proxy host, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_address
- # Port number of proxy host. If Net::HTTP does not use a proxy, nil.
+ # Returns the port number of the proxy host, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_port
- # User name for accessing proxy. If Net::HTTP does not use a proxy, nil.
+ # Returns the user name for accessing the proxy, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_user
- # User password for accessing proxy. If Net::HTTP does not use a proxy,
- # nil.
+ # Returns the password for accessing the proxy, or +nil+ if none;
+ # see Net::HTTP@Proxy+Server.
attr_reader :proxy_pass
end
- # True if requests for this connection will be proxied
+ # Returns +true+ if a proxy server is defined, +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy?
!!(@proxy_from_env ? proxy_uri : @proxy_address)
end
- # True if the proxy for this connection is determined from the environment
+ # Returns +true+ if the proxy server is defined in the environment,
+ # +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_from_env?
@proxy_from_env
end
@@ -1198,12 +1797,13 @@ module Net #:nodoc:
def proxy_uri # :nodoc:
return if @proxy_uri == false
@proxy_uri ||= URI::HTTP.new(
- "http".freeze, nil, address, port, nil, nil, nil, nil, nil
+ "http", nil, address, port, nil, nil, nil, nil, nil
).find_proxy || false
@proxy_uri || nil
end
- # The address of the proxy server, if one is configured.
+ # Returns the address of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_address
if @proxy_from_env then
proxy_uri&.hostname
@@ -1212,7 +1812,8 @@ module Net #:nodoc:
end
end
- # The port of the proxy server, if one is configured.
+ # Returns the port number of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_port
if @proxy_from_env then
proxy_uri&.port
@@ -1221,16 +1822,10 @@ module Net #:nodoc:
end
end
- # [Bug #12921]
- if /linux|freebsd|darwin/ =~ RUBY_PLATFORM
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = true
- else
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = false
- end
-
- # The username of the proxy server, if one is configured.
+ # Returns the user name of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_user
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ if @proxy_from_env
user = proxy_uri&.user
unescape(user) if user
else
@@ -1238,9 +1833,10 @@ module Net #:nodoc:
end
end
- # The password of the proxy server, if one is configured.
+ # Returns the password of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
def proxy_pass
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ if @proxy_from_env
pass = proxy_uri&.password
unescape(pass) if pass
else
@@ -1286,45 +1882,38 @@ module Net #:nodoc:
public
- # Retrieves data from +path+ on the connected-to host which may be an
- # absolute path String or a URI to extract the path from.
+ # :call-seq:
+ # get(path, initheader = nil) {|res| ... }
+ #
+ # Sends a GET request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
#
- # +initheader+ must be a Hash like { 'Accept' => '*/*', ... },
- # and it defaults to an empty hash.
- # If +initheader+ doesn't have the key 'accept-encoding', then
- # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used,
- # so that gzip compression is used in preference to deflate
- # compression, which is used in preference to no compression.
- # Ruby doesn't have libraries to support the compress (Lempel-Ziv)
- # compression, so that is not supported. The intent of this is
- # to reduce bandwidth by default. If this routine sets up
- # compression, then it does the decompression also, removing
- # the header as well to prevent confusion. Otherwise
- # it leaves the body as it found it.
+ # http = Net::HTTP.new(hostname)
+ # http.get('/todos/1') do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # This method returns a Net::HTTPResponse object.
+ # Output:
#
- # If called with a block, yields each fragment of the
- # entity body in turn as a string as it is read from
- # the socket. Note that in this case, the returned response
- # object will *not* contain a (meaningful) body.
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
#
- # +dest+ argument is obsolete.
- # It still works but you must not use it.
+ # With no block given, simply returns the response object:
#
- # This method never raises an exception.
+ # http.get('/') # => #<Net::HTTPOK 200 OK readbody=true>
#
- # response = http.get('/index.html')
+ # Related:
#
- # # using block
- # File.open('result.txt', 'w') {|f|
- # http.get('/~foo/') do |str|
- # f.write str
- # end
- # }
+ # - Net::HTTP::Get: request class for \HTTP method GET.
+ # - Net::HTTP.get: sends GET request, returns response body.
#
def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
res = nil
+
request(Get.new(path, initheader)) {|r|
r.read_body dest, &block
res = r
@@ -1332,198 +1921,312 @@ module Net #:nodoc:
res
end
- # Gets only the header from +path+ on the connected-to host.
- # +header+ is a Hash like { 'Accept' => '*/*', ... }.
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # This method returns a Net::HTTPResponse object.
+ # The request is based on the Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+:
#
- # This method never raises an exception.
- #
- # response = nil
- # Net::HTTP.start('some.www.server', 80) {|http|
- # response = http.head('/index.html')
- # }
- # p response['content-type']
+ # res = http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
+ # res.body # => nil
+ # res.to_hash.take(3)
+ # # =>
+ # [["date", ["Wed, 15 Feb 2023 15:25:42 GMT"]],
+ # ["content-type", ["application/json; charset=utf-8"]],
+ # ["connection", ["close"]]]
#
def head(path, initheader = nil)
request(Head.new(path, initheader))
end
- # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
- # like { 'Accept' => '*/*', ... }.
+ # :call-seq:
+ # post(path, data, initheader = nil) {|res| ... }
#
- # This method returns a Net::HTTPResponse object.
+ # Sends a POST request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # If called with a block, yields each fragment of the
- # entity body in turn as a string as it is read from
- # the socket. Note that in this case, the returned response
- # object will *not* contain a (meaningful) body.
+ # The request is based on the Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
#
- # +dest+ argument is obsolete.
- # It still works but you must not use it.
+ # With a block given, calls the block with the response body:
#
- # This method never raises exception.
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.post('/todos', data) do |res|
+ # p res
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\",\n \"id\": 201\n}"
+ #
+ # With no block given, simply returns the response object:
#
- # response = http.post('/cgi-bin/search.rb', 'query=foo')
+ # http.post('/todos', data) # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # # using block
- # File.open('result.txt', 'w') {|f|
- # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
- # f.write str
- # end
- # }
+ # Related:
#
- # You should set Content-Type: header field for POST.
- # If no Content-Type: field given, this method uses
- # "application/x-www-form-urlencoded" by default.
+ # - Net::HTTP::Post: request class for \HTTP method POST.
+ # - Net::HTTP.post: sends POST request, returns response body.
#
def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
send_entity(path, data, initheader, dest, Post, &block)
end
- # Sends a PATCH request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # :call-seq:
+ # patch(path, data, initheader = nil) {|res| ... }
+ #
+ # Sends a PATCH request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Patch object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.patch('/todos/1', data) do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false,\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\"\n}"
+ #
+ # With no block given, simply returns the response object:
+ #
+ # http.patch('/todos/1', data) # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
send_entity(path, data, initheader, dest, Patch, &block)
end
- def put(path, data, initheader = nil) #:nodoc:
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.put('/todos/1', data) # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ def put(path, data, initheader = nil)
request(Put.new(path, initheader), data)
end
- # Sends a PROPPATCH request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a PROPPATCH request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Proppatch object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.proppatch('/todos/1', data)
+ #
def proppatch(path, body, initheader = nil)
request(Proppatch.new(path, initheader), body)
end
- # Sends a LOCK request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a LOCK request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Lock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.lock('/todos/1', data)
+ #
def lock(path, body, initheader = nil)
request(Lock.new(path, initheader), body)
end
- # Sends a UNLOCK request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends an UNLOCK request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Unlock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.unlock('/todos/1', data)
+ #
def unlock(path, body, initheader = nil)
request(Unlock.new(path, initheader), body)
end
- # Sends a OPTIONS request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends an Options request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Options object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.options('/')
+ #
def options(path, initheader = nil)
request(Options.new(path, initheader))
end
- # Sends a PROPFIND request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a PROPFIND request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Propfind object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Net::HTTP.new(hostname)
+ # http.propfind('/todos/1', data)
+ #
def propfind(path, body = nil, initheader = {'Depth' => '0'})
request(Propfind.new(path, initheader), body)
end
- # Sends a DELETE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a DELETE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Delete object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.delete('/todos/1')
+ #
def delete(path, initheader = {'Depth' => 'Infinity'})
request(Delete.new(path, initheader))
end
- # Sends a MOVE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a MOVE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Move object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.move('/todos/1')
+ #
def move(path, initheader = nil)
request(Move.new(path, initheader))
end
- # Sends a COPY request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a COPY request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Copy object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.copy('/todos/1')
+ #
def copy(path, initheader = nil)
request(Copy.new(path, initheader))
end
- # Sends a MKCOL request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a MKCOL request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Mkcol object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http.mkcol('/todos/1', data)
+ # http = Net::HTTP.new(hostname)
+ #
def mkcol(path, body = nil, initheader = nil)
request(Mkcol.new(path, initheader), body)
end
- # Sends a TRACE request to the +path+ and gets a response,
- # as an HTTPResponse object.
+ # Sends a TRACE request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Trace object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.trace('/todos/1')
+ #
def trace(path, initheader = nil)
request(Trace.new(path, initheader))
end
- # Sends a GET request to the +path+.
- # Returns the response as a Net::HTTPResponse object.
+ # Sends a GET request to the server;
+ # forms the response into a Net::HTTPResponse object.
#
- # When called with a block, passes an HTTPResponse object to the block.
- # The body of the response will not have been read yet;
- # the block can process it using HTTPResponse#read_body,
- # if desired.
+ # The request is based on the Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
#
- # Returns the response.
+ # With no block given, returns the response object:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.request_get('/todos') # => #<Net::HTTPOK 200 OK readbody=true>
#
- # This method never raises Net::* exceptions.
+ # With a block given, calls the block with the response object
+ # and returns the response object:
#
- # response = http.request_get('/index.html')
- # # The entity body is already read in this case.
- # p response['content-type']
- # puts response.body
+ # http.request_get('/todos') do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # # Using a block
- # http.request_get('/index.html') {|response|
- # p response['content-type']
- # response.read_body do |str| # read body now
- # print str
- # end
- # }
+ # Output:
+ #
+ # #<Net::HTTPOK 200 OK readbody=false>
#
def request_get(path, initheader = nil, &block) # :yield: +response+
request(Get.new(path, initheader), &block)
end
- # Sends a HEAD request to the +path+ and returns the response
- # as a Net::HTTPResponse object.
- #
- # Returns the response.
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # This method never raises Net::* exceptions.
+ # The request is based on the Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+.
#
- # response = http.request_head('/index.html')
- # p response['content-type']
+ # http = Net::HTTP.new(hostname)
+ # http.head('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
#
def request_head(path, initheader = nil, &block)
request(Head.new(path, initheader), &block)
end
- # Sends a POST request to the +path+.
+ # Sends a POST request to the server;
+ # forms the response into a Net::HTTPResponse object.
#
- # Returns the response as a Net::HTTPResponse object.
+ # The request is based on the Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
#
- # When called with a block, the block is passed an HTTPResponse
- # object. The body of that response will not have been read yet;
- # the block can process it using HTTPResponse#read_body, if desired.
+ # With no block given, returns the response object:
#
- # Returns the response.
+ # http = Net::HTTP.new(hostname)
+ # http.post('/todos', 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
+ # With a block given, calls the block with the response body
+ # and returns the response object:
#
- # This method never raises Net::* exceptions.
+ # http.post('/todos', 'xyzzy') do |res|
+ # p res
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # # example
- # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
- # p response.status
- # puts response.body # body is already read in this case
+ # Output:
#
- # # using block
- # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
- # p response.status
- # p response['content-type']
- # response.read_body do |str| # read body now
- # print str
- # end
- # }
+ # "{\n \"xyzzy\": \"\",\n \"id\": 201\n}"
#
def request_post(path, data, initheader = nil, &block) # :yield: +response+
request Post.new(path, initheader), data, &block
end
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
+ #
+ # The request is based on the Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.put('/todos/1', 'xyzzy')
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ #
def request_put(path, data, initheader = nil, &block) #:nodoc:
request Put.new(path, initheader), data, &block
end
@@ -1533,16 +2236,25 @@ module Net #:nodoc:
alias post2 request_post #:nodoc: obsolete
alias put2 request_put #:nodoc: obsolete
-
- # Sends an HTTP request to the HTTP server.
- # Also sends a DATA string if +data+ is given.
+ # Sends an \HTTP request to the server;
+ # returns an instance of a subclass of Net::HTTPResponse.
#
- # Returns a Net::HTTPResponse object.
+ # The request is based on the Net::HTTPRequest object
+ # created from string +path+, string +data+, and initial headers hash +header+.
+ # That object is an instance of the
+ # {subclass of Net::HTTPRequest}[rdoc-ref:Net::HTTPRequest@Request+Subclasses],
+ # that corresponds to the given uppercase string +name+,
+ # which must be
+ # an {HTTP request method}[https://en.wikipedia.org/wiki/HTTP#Request_methods]
+ # or a {WebDAV request method}[https://en.wikipedia.org/wiki/WebDAV#Implementation].
#
- # This method never raises Net::* exceptions.
+ # Examples:
#
- # response = http.send_request('GET', '/index.html')
- # puts response.body
+ # http = Net::HTTP.new(hostname)
+ # http.send_request('GET', '/todos/1')
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ # http.send_request('POST', '/todos', 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
#
def send_request(name, path, data = nil, header = nil)
has_response_body = name != 'HEAD'
@@ -1550,20 +2262,35 @@ module Net #:nodoc:
request r, data
end
- # Sends an HTTPRequest object +req+ to the HTTP server.
+ # Sends the given request +req+ to the server;
+ # forms the response into a Net::HTTPResponse object.
+ #
+ # The given +req+ must be an instance of a
+ # {subclass of Net::HTTPRequest}[rdoc-ref:Net::HTTPRequest@Request+Subclasses].
+ # Argument +body+ should be given only if needed for the request.
+ #
+ # With no block given, returns the response object:
+ #
+ # http = Net::HTTP.new(hostname)
+ #
+ # req = Net::HTTP::Get.new('/todos/1')
+ # http.request(req)
+ # # => #<Net::HTTPOK 200 OK readbody=true>
+ #
+ # req = Net::HTTP::Post.new('/todos')
+ # http.request(req, 'xyzzy')
+ # # => #<Net::HTTPCreated 201 Created readbody=true>
#
- # If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing
- # data, the data is also sent. Providing data for a Net::HTTP::Head or
- # Net::HTTP::Get request results in an ArgumentError.
+ # With a block given, calls the block with the response and returns the response:
#
- # Returns an HTTPResponse object.
+ # req = Net::HTTP::Get.new('/todos/1')
+ # http.request(req) do |res|
+ # p res
+ # end # => #<Net::HTTPOK 200 OK readbody=true>
#
- # When called with a block, passes an HTTPResponse object to the block.
- # The body of the response will not have been read yet;
- # the block can process it using HTTPResponse#read_body,
- # if desired.
+ # Output:
#
- # This method never raises Net::* exceptions.
+ # #<Net::HTTPOK 200 OK readbody=false>
#
def request(req, body = nil, &block) # :yield: +response+
unless started?
diff --git a/lib/net/http/backward.rb b/lib/net/http/backward.rb
index 691e41e4f1..b44577edbd 100644
--- a/lib/net/http/backward.rb
+++ b/lib/net/http/backward.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# for backward compatibility
# :enddoc:
diff --git a/lib/net/http/exceptions.rb b/lib/net/http/exceptions.rb
index 9c425cae16..ceec8f7b0a 100644
--- a/lib/net/http/exceptions.rb
+++ b/lib/net/http/exceptions.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net
# Net::HTTP exception class.
# You cannot use Net::HTTPExceptions directly; instead, you must use
diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb
index 313de6ac92..44e329a0c8 100644
--- a/lib/net/http/generic_request.rb
+++ b/lib/net/http/generic_request.rb
@@ -1,24 +1,29 @@
-# frozen_string_literal: false
-# HTTPGenericRequest is the parent of the Net::HTTPRequest class.
-# Do not use this directly; use a subclass of Net::HTTPRequest.
+# frozen_string_literal: true
#
-# Mixes in the Net::HTTPHeader module to provide easier access to HTTP headers.
+# \HTTPGenericRequest is the parent of the Net::HTTPRequest class.
+#
+# Do not use this directly; instead, use a subclass of Net::HTTPRequest.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
#
class Net::HTTPGenericRequest
include Net::HTTPHeader
- def initialize(m, reqbody, resbody, uri_or_path, initheader = nil)
+ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc:
@method = m
@request_has_body = reqbody
@response_has_body = resbody
if URI === uri_or_path then
raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path
- raise ArgumentError, "no host component for URI" unless uri_or_path.hostname
+ hostname = uri_or_path.hostname
+ raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0)
@uri = uri_or_path.dup
host = @uri.hostname.dup
- host << ":".freeze << @uri.port.to_s if @uri.port != @uri.default_port
+ host << ":" << @uri.port.to_s if @uri.port != @uri.default_port
@path = uri_or_path.request_uri
raise ArgumentError, "no HTTP request path given" unless @path
else
@@ -52,15 +57,47 @@ class Net::HTTPGenericRequest
@body_data = nil
end
+ # Returns the string method name for the request:
+ #
+ # Net::HTTP::Get.new(uri).method # => "GET"
+ # Net::HTTP::Post.new(uri).method # => "POST"
+ #
attr_reader :method
+
+ # Returns the string path for the request:
+ #
+ # Net::HTTP::Get.new(uri).path # => "/"
+ # Net::HTTP::Post.new('example.com').path # => "example.com"
+ #
attr_reader :path
+
+ # Returns the URI object for the request, or +nil+ if none:
+ #
+ # Net::HTTP::Get.new(uri).uri
+ # # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Net::HTTP::Get.new('example.com').uri # => nil
+ #
attr_reader :uri
- # Automatically set to false if the user sets the Accept-Encoding header.
- # This indicates they wish to handle Content-encoding in responses
- # themselves.
+ # Returns +false+ if the request's header <tt>'Accept-Encoding'</tt>
+ # has been set manually or deleted
+ # (indicating that the user intends to handle encoding in the response),
+ # +true+ otherwise:
+ #
+ # req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+ # req['Accept-Encoding'] # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ # req.decode_content # => true
+ # req['Accept-Encoding'] = 'foo'
+ # req.decode_content # => false
+ # req.delete('Accept-Encoding')
+ # req.decode_content # => false
+ #
attr_reader :decode_content
+ # Returns a string representation of the request:
+ #
+ # Net::HTTP::Post.new(uri).inspect # => "#<Net::HTTP::Post POST>"
+ #
def inspect
"\#<#{self.class} #{@method}>"
end
@@ -75,21 +112,45 @@ class Net::HTTPGenericRequest
super key, val
end
+ # Returns whether the request may have a body:
+ #
+ # Net::HTTP::Post.new(uri).request_body_permitted? # => true
+ # Net::HTTP::Get.new(uri).request_body_permitted? # => false
+ #
def request_body_permitted?
@request_has_body
end
+ # Returns whether the response may have a body:
+ #
+ # Net::HTTP::Post.new(uri).response_body_permitted? # => true
+ # Net::HTTP::Head.new(uri).response_body_permitted? # => false
+ #
def response_body_permitted?
@response_has_body
end
- def body_exist?
+ def body_exist? # :nodoc:
warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE
response_body_permitted?
end
+ # Returns the string body for the request, or +nil+ if there is none:
+ #
+ # req = Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
attr_reader :body
+ # Sets the body for the request:
+ #
+ # req = Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
def body=(str)
@body = str
@body_stream = nil
@@ -97,8 +158,24 @@ class Net::HTTPGenericRequest
str
end
+ # Returns the body stream object for the request, or +nil+ if there is none:
+ #
+ # req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
attr_reader :body_stream
+ # Sets the body stream for the request:
+ #
+ # req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
def body_stream=(input)
@body = nil
@body_stream = input
@@ -135,15 +212,15 @@ class Net::HTTPGenericRequest
return unless @uri
if ssl
- scheme = 'https'.freeze
+ scheme = 'https'
klass = URI::HTTPS
else
- scheme = 'http'.freeze
+ scheme = 'http'
klass = URI::HTTP
end
if host = self['host']
- host.sub!(/:.*/m, ''.freeze)
+ host.sub!(/:.*/m, '')
elsif host = @uri.host
else
host = addr
@@ -239,7 +316,7 @@ class Net::HTTPGenericRequest
boundary ||= SecureRandom.urlsafe_base64(40)
chunked_p = chunked?
- buf = ''
+ buf = +''
params.each do |key, value, h={}|
key = quote_string(key, charset)
filename =
@@ -324,7 +401,7 @@ class Net::HTTPGenericRequest
if /[\r\n]/ =~ reqline
raise ArgumentError, "A Request-Line must not contain CR or LF"
end
- buf = ""
+ buf = +''
buf << reqline << "\r\n"
each_capitalized do |k,v|
buf << "#{k}: #{v}\r\n"
diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index b0ec4b0625..6660c8075a 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -1,16 +1,188 @@
-# frozen_string_literal: false
-# The HTTPHeader module defines methods for reading and writing
-# HTTP headers.
+# frozen_string_literal: true
#
-# It is used as a mixin by other classes, to provide hash-like
-# access to HTTP header values. Unlike raw hash access, HTTPHeader
-# provides access via case-insensitive keys. It also provides
-# methods for accessing commonly-used HTTP header values in more
-# convenient formats.
+# The \HTTPHeader module provides access to \HTTP headers.
+#
+# The module is included in:
+#
+# - Net::HTTPGenericRequest (and therefore Net::HTTPRequest).
+# - Net::HTTPResponse.
+#
+# The headers are a hash-like collection of key/value pairs called _fields_.
+#
+# == Request and Response Fields
+#
+# Headers may be included in:
+#
+# - A Net::HTTPRequest object:
+# the object's headers will be sent with the request.
+# Any fields may be defined in the request;
+# see {Setters}[rdoc-ref:Net::HTTPHeader@Setters].
+# - A Net::HTTPResponse object:
+# the objects headers are usually those returned from the host.
+# Fields may be retrieved from the object;
+# see {Getters}[rdoc-ref:Net::HTTPHeader@Getters]
+# and {Iterators}[rdoc-ref:Net::HTTPHeader@Iterators].
+#
+# Exactly which fields should be sent or expected depends on the host;
+# see:
+#
+# - {Request fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+# - {Response fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields].
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Fields
+#
+# A header field is a key/value pair.
+#
+# === Field Keys
+#
+# A field key may be:
+#
+# - A string: Key <tt>'Accept'</tt> is treated as if it were
+# <tt>'Accept'.downcase</tt>; i.e., <tt>'accept'</tt>.
+# - A symbol: Key <tt>:Accept</tt> is treated as if it were
+# <tt>:Accept.to_s.downcase</tt>; i.e., <tt>'accept'</tt>.
+#
+# Examples:
+#
+# req = Net::HTTP::Get.new(uri)
+# req[:accept] # => "*/*"
+# req['Accept'] # => "*/*"
+# req['ACCEPT'] # => "*/*"
+#
+# req['accept'] = 'text/html'
+# req[:accept] = 'text/html'
+# req['ACCEPT'] = 'text/html'
+#
+# === Field Values
+#
+# A field value may be returned as an array of strings or as a string:
+#
+# - These methods return field values as arrays:
+#
+# - #get_fields: Returns the array value for the given key,
+# or +nil+ if it does not exist.
+# - #to_hash: Returns a hash of all header fields:
+# each key is a field name; its value is the array value for the field.
+#
+# - These methods return field values as string;
+# the string value for a field is equivalent to
+# <tt>self[key.downcase.to_s].join(', '))</tt>:
+#
+# - #[]: Returns the string value for the given key,
+# or +nil+ if it does not exist.
+# - #fetch: Like #[], but accepts a default value
+# to be returned if the key does not exist.
+#
+# The field value may be set:
+#
+# - #[]=: Sets the value for the given key;
+# the given value may be a string, a symbol, an array, or a hash.
+# - #add_field: Adds a given value to a value for the given key
+# (not overwriting the existing value).
+# - #delete: Deletes the field for the given key.
+#
+# Example field values:
+#
+# - \String:
+#
+# req['Accept'] = 'text/html' # => "text/html"
+# req['Accept'] # => "text/html"
+# req.get_fields('Accept') # => ["text/html"]
+#
+# - \Symbol:
+#
+# req['Accept'] = :text # => :text
+# req['Accept'] # => "text"
+# req.get_fields('Accept') # => ["text"]
+#
+# - Simple array:
+#
+# req[:foo] = %w[bar baz bat]
+# req[:foo] # => "bar, baz, bat"
+# req.get_fields(:foo) # => ["bar", "baz", "bat"]
+#
+# - Simple hash:
+#
+# req[:foo] = {bar: 0, baz: 1, bat: 2}
+# req[:foo] # => "bar, 0, baz, 1, bat, 2"
+# req.get_fields(:foo) # => ["bar", "0", "baz", "1", "bat", "2"]
+#
+# - Nested:
+#
+# req[:foo] = [%w[bar baz], {bat: 0, bam: 1}]
+# req[:foo] # => "bar, baz, bat, 0, bam, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "0", "bam", "1"]
+#
+# req[:foo] = {bar: %w[baz bat], bam: {bah: 0, bad: 1}}
+# req[:foo] # => "bar, baz, bat, bam, bah, 0, bad, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "bam", "bah", "0", "bad", "1"]
+#
+# == Convenience Methods
+#
+# Various convenience methods retrieve values, set values, query values,
+# set form values, or iterate over fields.
+#
+# === Setters
+#
+# \Method #[]= can set any field, but does little to validate the new value;
+# some of the other setter methods provide some validation:
+#
+# - #[]=: Sets the string or array value for the given key.
+# - #add_field: Creates or adds to the array value for the given key.
+# - #basic_auth: Sets the string authorization header for <tt>'Authorization'</tt>.
+# - #content_length=: Sets the integer length for field <tt>'Content-Length</tt>.
+# - #content_type=: Sets the string value for field <tt>'Content-Type'</tt>.
+# - #proxy_basic_auth: Sets the string authorization header for <tt>'Proxy-Authorization'</tt>.
+# - #set_range: Sets the value for field <tt>'Range'</tt>.
+#
+# === Form Setters
+#
+# - #set_form: Sets an HTML form data set.
+# - #set_form_data: Sets header fields and a body from HTML form data.
+#
+# === Getters
+#
+# \Method #[] can retrieve the value of any field that exists,
+# but always as a string;
+# some of the other getter methods return something different
+# from the simple string value:
+#
+# - #[]: Returns the string field value for the given key.
+# - #content_length: Returns the integer value of field <tt>'Content-Length'</tt>.
+# - #content_range: Returns the Range value of field <tt>'Content-Range'</tt>.
+# - #content_type: Returns the string value of field <tt>'Content-Type'</tt>.
+# - #fetch: Returns the string field value for the given key.
+# - #get_fields: Returns the array field value for the given +key+.
+# - #main_type: Returns first part of the string value of field <tt>'Content-Type'</tt>.
+# - #sub_type: Returns second part of the string value of field <tt>'Content-Type'</tt>.
+# - #range: Returns an array of Range objects of field <tt>'Range'</tt>, or +nil+.
+# - #range_length: Returns the integer length of the range given in field <tt>'Content-Range'</tt>.
+# - #type_params: Returns the string parameters for <tt>'Content-Type'</tt>.
+#
+# === Queries
+#
+# - #chunked?: Returns whether field <tt>'Transfer-Encoding'</tt> is set to <tt>'chunked'</tt>.
+# - #connection_close?: Returns whether field <tt>'Connection'</tt> is set to <tt>'close'</tt>.
+# - #connection_keep_alive?: Returns whether field <tt>'Connection'</tt> is set to <tt>'keep-alive'</tt>.
+# - #key?: Returns whether a given key exists.
+#
+# === Iterators
+#
+# - #each_capitalized: Passes each field capitalized-name/value pair to the block.
+# - #each_capitalized_name: Passes each capitalized field name to the block.
+# - #each_header: Passes each field name/value pair to the block.
+# - #each_name: Passes each field name to the block.
+# - #each_value: Passes each string field value to the block.
#
module Net::HTTPHeader
+ MAX_KEY_LENGTH = 1024
+ MAX_FIELD_LENGTH = 65536
- def initialize_http_header(initheader)
+ def initialize_http_header(initheader) #:nodoc:
@header = {}
return unless initheader
initheader.each do |key, value|
@@ -19,6 +191,12 @@ module Net::HTTPHeader
warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
else
value = value.strip # raise error for invalid byte sequences
+ if key.to_s.bytesize > MAX_KEY_LENGTH
+ raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
+ end
+ if value.to_s.bytesize > MAX_FIELD_LENGTH
+ raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
+ end
if value.count("\r\n") > 0
raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
end
@@ -33,14 +211,32 @@ module Net::HTTPHeader
alias length size #:nodoc: obsolete
- # Returns the header field corresponding to the case-insensitive key.
- # For example, a key of "Content-Type" might return "text/html"
+ # Returns the string field value for the case-insensitive field +key+,
+ # or +nil+ if there is no such key;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Connection'] # => "keep-alive"
+ # res['Nosuch'] # => nil
+ #
+ # Note that some field values may be retrieved via convenience methods;
+ # see {Getters}[rdoc-ref:Net::HTTPHeader@Getters].
def [](key)
a = @header[key.downcase.to_s] or return nil
a.join(', ')
end
- # Sets the header field corresponding to the case-insensitive key.
+ # Sets the value for the case-insensitive +key+ to +val+,
+ # overwriting the previous value if the field exists;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Accept'] # => "*/*"
+ # req['Accept'] = 'text/html'
+ # req['Accept'] # => "text/html"
+ #
+ # Note that some field values may be set via convenience methods;
+ # see {Setters}[rdoc-ref:Net::HTTPHeader@Setters].
def []=(key, val)
unless val
@header.delete key.downcase.to_s
@@ -49,20 +245,18 @@ module Net::HTTPHeader
set_field(key, val)
end
- # [Ruby 1.8.3]
- # Adds a value to a named header field, instead of replacing its value.
- # Second argument +val+ must be a String.
- # See also #[]=, #[] and #get_fields.
+ # Adds value +val+ to the value array for field +key+ if the field exists;
+ # creates the field with the given +key+ and +val+ if it does not exist.
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
#
- # request.add_field 'X-My-Header', 'a'
- # p request['X-My-Header'] #=> "a"
- # p request.get_fields('X-My-Header') #=> ["a"]
- # request.add_field 'X-My-Header', 'b'
- # p request['X-My-Header'] #=> "a, b"
- # p request.get_fields('X-My-Header') #=> ["a", "b"]
- # request.add_field 'X-My-Header', 'c'
- # p request['X-My-Header'] #=> "a, b, c"
- # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
+ # req = Net::HTTP::Get.new(uri)
+ # req.add_field('Foo', 'bar')
+ # req['Foo'] # => "bar"
+ # req.add_field('Foo', 'baz')
+ # req['Foo'] # => "bar, baz"
+ # req.add_field('Foo', %w[baz bam])
+ # req['Foo'] # => "bar, baz, baz, bam"
+ # req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"]
#
def add_field(key, val)
stringified_downcased_key = key.downcase.to_s
@@ -101,16 +295,13 @@ module Net::HTTPHeader
end
end
- # [Ruby 1.8.3]
- # Returns an array of header field strings corresponding to the
- # case-insensitive +key+. This method allows you to get duplicated
- # header fields without any processing. See also #[].
+ # Returns the array field value for the given +key+,
+ # or +nil+ if there is no such field;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
#
- # p response.get_fields('Set-Cookie')
- # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
- # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
- # p response['Set-Cookie']
- # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.get_fields('Connection') # => ["keep-alive"]
+ # res.get_fields('Nosuch') # => nil
#
def get_fields(key)
stringified_downcased_key = key.downcase.to_s
@@ -118,24 +309,58 @@ module Net::HTTPHeader
@header[stringified_downcased_key].dup
end
- # Returns the header field corresponding to the case-insensitive key.
- # Returns the default value +args+, or the result of the block, or
- # raises an IndexError if there's no header field named +key+
- # See Hash#fetch
+ # call-seq:
+ # fetch(key, default_val = nil) {|key| ... } -> object
+ # fetch(key, default_val = nil) -> value or default_val
+ #
+ # With a block, returns the string value for +key+ if it exists;
+ # otherwise returns the value of the block;
+ # ignores the +default_val+;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ #
+ # # Field exists; block not called.
+ # res.fetch('Connection') do |value|
+ # fail 'Cannot happen'
+ # end # => "keep-alive"
+ #
+ # # Field does not exist; block called.
+ # res.fetch('Nosuch') do |value|
+ # value.downcase
+ # end # => "nosuch"
+ #
+ # With no block, returns the string value for +key+ if it exists;
+ # otherwise, returns +default_val+ if it was given;
+ # otherwise raises an exception:
+ #
+ # res.fetch('Connection', 'Foo') # => "keep-alive"
+ # res.fetch('Nosuch', 'Foo') # => "Foo"
+ # res.fetch('Nosuch') # Raises KeyError.
+ #
def fetch(key, *args, &block) #:yield: +key+
a = @header.fetch(key.downcase.to_s, *args, &block)
a.kind_of?(Array) ? a.join(', ') : a
end
- # Iterates through the header names and values, passing in the name
- # and value to the code block supplied.
+ # Calls the block with each key/value pair:
#
- # Returns an enumerator if no block is given.
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_header do |key, value|
+ # p [key, value] if key.start_with?('c')
+ # end
#
- # Example:
+ # Output:
#
- # response.header.each_header {|key,value| puts "#{key} = #{value}" }
+ # ["content-type", "application/json; charset=utf-8"]
+ # ["connection", "keep-alive"]
+ # ["cache-control", "max-age=43200"]
+ # ["cf-cache-status", "HIT"]
+ # ["cf-ray", "771d17e9bc542cf5-ORD"]
#
+ # Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#each is an alias for Net::HTTPHeader#each_header.
def each_header #:yield: +key+, +value+
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,va|
@@ -145,10 +370,24 @@ module Net::HTTPHeader
alias each each_header
- # Iterates through the header names in the header, passing
- # each header name to the code block.
+ # Calls the block with each field key:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_key do |key|
+ # p key if key.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "content-type"
+ # "connection"
+ # "cache-control"
+ # "cf-cache-status"
+ # "cf-ray"
#
# Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#each_name is an alias for Net::HTTPHeader#each_key.
def each_name(&block) #:yield: +key+
block_given? or return enum_for(__method__) { @header.size }
@header.each_key(&block)
@@ -156,12 +395,23 @@ module Net::HTTPHeader
alias each_key each_name
- # Iterates through the header names in the header, passing
- # capitalized header names to the code block.
+ # Calls the block with each capitalized field name:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_capitalized_name do |key|
+ # p key if key.start_with?('C')
+ # end
+ #
+ # Output:
#
- # Note that header names are capitalized systematically;
- # capitalization may not match that used by the remote HTTP
- # server in its response.
+ # "Content-Type"
+ # "Connection"
+ # "Cache-Control"
+ # "Cf-Cache-Status"
+ # "Cf-Ray"
+ #
+ # The capitalization is system-dependent;
+ # see {Case Mapping}[rdoc-ref:case_mapping.rdoc].
#
# Returns an enumerator if no block is given.
def each_capitalized_name #:yield: +key+
@@ -171,8 +421,18 @@ module Net::HTTPHeader
end
end
- # Iterates through header values, passing each value to the
- # code block.
+ # Calls the block with each string field value:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_value do |value|
+ # p value if value.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "chunked"
+ # "cf-q-config;dur=6.0000002122251e-06"
+ # "cloudflare"
#
# Returns an enumerator if no block is given.
def each_value #:yield: +value+
@@ -182,32 +442,45 @@ module Net::HTTPHeader
end
end
- # Removes a header field, specified by case-insensitive key.
+ # Removes the header for the given case-insensitive +key+
+ # (see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]);
+ # returns the deleted value, or +nil+ if no such field exists:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.delete('Accept') # => ["*/*"]
+ # req.delete('Nosuch') # => nil
+ #
def delete(key)
@header.delete(key.downcase.to_s)
end
- # true if +key+ header exists.
+ # Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.key?('Accept') # => true
+ # req.key?('Nosuch') # => false
+ #
def key?(key)
@header.key?(key.downcase.to_s)
end
- # Returns a Hash consisting of header names and array of values.
- # e.g.
- # {"cache-control" => ["private"],
- # "content-type" => ["text/html"],
- # "date" => ["Wed, 22 Jun 2005 22:11:50 GMT"]}
+ # Returns a hash of the key/value pairs:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.to_hash
+ # # =>
+ # {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+ # "accept"=>["*/*"],
+ # "user-agent"=>["Ruby"],
+ # "host"=>["jsonplaceholder.typicode.com"]}
+ #
def to_hash
@header.dup
end
- # As for #each_header, except the keys are provided in capitalized form.
+ # Like #each_header, but the keys are returned in capitalized form.
#
- # Note that header names are capitalized systematically;
- # capitalization may not match that used by the remote HTTP
- # server in its response.
- #
- # Returns an enumerator if no block is given.
+ # Net::HTTPHeader#canonical_each is an alias for Net::HTTPHeader#each_capitalized.
def each_capitalized
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,v|
@@ -222,8 +495,17 @@ module Net::HTTPHeader
end
private :capitalize
- # Returns an Array of Range objects which represent the Range:
- # HTTP header field, or +nil+ if there is no such header.
+ # Returns an array of Range objects that represent
+ # the value of field <tt>'Range'</tt>,
+ # or +nil+ if there is no such field;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Range'] = 'bytes=0-99,200-299,400-499'
+ # req.range # => [0..99, 200..299, 400..499]
+ # req.delete('Range')
+ # req.range # # => nil
+ #
def range
return nil unless @header['range']
@@ -266,14 +548,31 @@ module Net::HTTPHeader
result
end
- # Sets the HTTP Range: header.
- # Accepts either a Range object as a single argument,
- # or a beginning index and a length from that index.
- # Example:
+ # call-seq:
+ # set_range(length) -> length
+ # set_range(offset, length) -> range
+ # set_range(begin..length) -> range
+ #
+ # Sets the value for field <tt>'Range'</tt>;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # With argument +length+:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.set_range(100) # => 100
+ # req['Range'] # => "bytes=0-99"
#
- # req.range = (0..1023)
- # req.set_range 0, 1023
+ # With arguments +offset+ and +length+:
#
+ # req.set_range(100, 100) # => 100...200
+ # req['Range'] # => "bytes=100-199"
+ #
+ # With argument +range+:
+ #
+ # req.set_range(100..199) # => 100..199
+ # req['Range'] # => "bytes=100-199"
+ #
+ # Net::HTTPHeader#range= is an alias for Net::HTTPHeader#set_range.
def set_range(r, e = nil)
unless r
@header.delete 'range'
@@ -305,8 +604,15 @@ module Net::HTTPHeader
alias range= set_range
- # Returns an Integer object which represents the HTTP Content-Length:
- # header field, or +nil+ if that field was not provided.
+ # Returns the value of field <tt>'Content-Length'</tt> as an integer,
+ # or +nil+ if there is no such field;
+ # see {Content-Length request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-request-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/nosuch/1')
+ # res.content_length # => 2
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res.content_length # => nil
+ #
def content_length
return nil unless key?('Content-Length')
len = self['Content-Length'].slice(/\d+/) or
@@ -314,6 +620,20 @@ module Net::HTTPHeader
len.to_i
end
+ # Sets the value of field <tt>'Content-Length'</tt> to the given numeric;
+ # see {Content-Length response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-response-header]:
+ #
+ # _uri = uri.dup
+ # hostname = _uri.hostname # => "jsonplaceholder.typicode.com"
+ # _uri.path = '/posts' # => "/posts"
+ # req = Net::HTTP::Post.new(_uri) # => #<Net::HTTP::Post POST>
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.content_length = req.body.size # => 42
+ # req.content_type = 'application/json'
+ # res = Net::HTTP.start(hostname) do |http|
+ # http.request(req)
+ # end # => #<Net::HTTPCreated 201 Created readbody=true>
+ #
def content_length=(len)
unless len
@header.delete 'content-length'
@@ -322,20 +642,31 @@ module Net::HTTPHeader
@header['content-length'] = [len.to_i.to_s]
end
- # Returns "true" if the "transfer-encoding" header is present and
- # set to "chunked". This is an HTTP/1.1 feature, allowing
- # the content to be sent in "chunks" without at the outset
- # stating the entire content length.
+ # Returns +true+ if field <tt>'Transfer-Encoding'</tt>
+ # exists and has value <tt>'chunked'</tt>,
+ # +false+ otherwise;
+ # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Transfer-Encoding'] # => "chunked"
+ # res.chunked? # => true
+ #
def chunked?
return false unless @header['transfer-encoding']
field = self['Transfer-Encoding']
(/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
end
- # Returns a Range object which represents the value of the Content-Range:
- # header field.
- # For a partial entity body, this indicates where this fragment
- # fits inside the full entity body, as range of byte offsets.
+ # Returns a Range object representing the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res['Content-Range'] # => "bytes 0-499/1000"
+ # res.content_range # => 0..499
+ #
def content_range
return nil unless @header['content-range']
m = %r<\A\s*(\w+)\s+(\d+)-(\d+)/(\d+|\*)>.match(self['Content-Range']) or
@@ -344,32 +675,66 @@ module Net::HTTPHeader
m[2].to_i .. m[3].to_i
end
- # The length of the range represented in Content-Range: header.
+ # Returns the integer representing length of the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res.range_length # => 500
+ #
def range_length
r = content_range() or return nil
r.end - r.begin + 1
end
- # Returns a content type string such as "text/html".
- # This method returns nil if Content-Type: header field does not exist.
+ # Returns the {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.content_type # => "application/json"
+ #
def content_type
- return nil unless main_type()
- if sub_type()
- then "#{main_type()}/#{sub_type()}"
- else main_type()
+ main = main_type()
+ return nil unless main
+
+ sub = sub_type()
+ if sub
+ "#{main}/#{sub}"
+ else
+ main
end
end
- # Returns a content type string such as "text".
- # This method returns nil if Content-Type: header field does not exist.
+ # Returns the leading ('type') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.main_type # => "application"
+ #
def main_type
return nil unless @header['content-type']
self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
end
- # Returns a content type string such as "html".
- # This method returns nil if Content-Type: header field does not exist
- # or sub-type is not given (e.g. "Content-Type: text").
+ # Returns the trailing ('subtype') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.sub_type # => "json"
+ #
def sub_type
return nil unless @header['content-type']
_, sub = *self['Content-Type'].split(';').first.to_s.split('/')
@@ -377,9 +742,14 @@ module Net::HTTPHeader
sub.strip
end
- # Any parameters specified for the content type, returned as a Hash.
- # For example, a header of Content-Type: text/html; charset=EUC-JP
- # would result in type_params returning {'charset' => 'EUC-JP'}
+ # Returns the trailing ('parameters') part of the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.type_params # => {"charset"=>"utf-8"}
+ #
def type_params
result = {}
list = self['Content-Type'].to_s.split(';')
@@ -391,29 +761,54 @@ module Net::HTTPHeader
result
end
- # Sets the content type in an HTTP header.
- # The +type+ should be a full HTTP content type, e.g. "text/html".
- # The +params+ are an optional Hash of parameters to add after the
- # content type, e.g. {'charset' => 'iso-8859-1'}
+ # Sets the value of field <tt>'Content-Type'</tt>;
+ # returns the new value;
+ # see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.set_content_type('application/json') # => ["application/json"]
+ #
+ # Net::HTTPHeader#content_type= is an alias for Net::HTTPHeader#set_content_type.
def set_content_type(type, params = {})
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
end
alias content_type= set_content_type
- # Set header fields and a body from HTML form data.
- # +params+ should be an Array of Arrays or
- # a Hash containing HTML form data.
- # Optional argument +sep+ means data record separator.
+ # Sets the request body to a URL-encoded string derived from argument +params+,
+ # and sets request header field <tt>'Content-Type'</tt>
+ # to <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The resulting request is suitable for HTTP request +POST+ or +PUT+.
+ #
+ # Argument +params+ must be suitable for use as argument +enum+ to
+ # {URI.encode_www_form}[rdoc-ref:URI.encode_www_form].
+ #
+ # With only argument +params+ given,
+ # sets the body to a URL-encoded string with the default separator <tt>'&'</tt>:
+ #
+ # req = Net::HTTP::Post.new('example.com')
+ #
+ # req.set_form_data(q: 'ruby', lang: 'en')
+ # req.body # => "q=ruby&lang=en"
+ # req['Content-Type'] # => "application/x-www-form-urlencoded"
#
- # Values are URL encoded as necessary and the content-type is set to
- # application/x-www-form-urlencoded
+ # req.set_form_data([['q', 'ruby'], ['lang', 'en']])
+ # req.body # => "q=ruby&lang=en"
#
- # Example:
- # http.form_data = {"q" => "ruby", "lang" => "en"}
- # http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
- # http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
+ # req.set_form_data(q: ['ruby', 'perl'], lang: 'en')
+ # req.body # => "q=ruby&q=perl&lang=en"
#
+ # req.set_form_data([['q', 'ruby'], ['q', 'perl'], ['lang', 'en']])
+ # req.body # => "q=ruby&q=perl&lang=en"
+ #
+ # With string argument +sep+ also given,
+ # uses that string as the separator:
+ #
+ # req.set_form_data({q: 'ruby', lang: 'en'}, '|')
+ # req.body # => "q=ruby|lang=en"
+ #
+ # Net::HTTPHeader#form_data= is an alias for Net::HTTPHeader#set_form_data.
def set_form_data(params, sep = '&')
query = URI.encode_www_form(params)
query.gsub!(/&/, sep) if sep != '&'
@@ -423,53 +818,108 @@ module Net::HTTPHeader
alias form_data= set_form_data
- # Set an HTML form data set.
- # +params+ :: The form data to set, which should be an enumerable.
- # See below for more details.
- # +enctype+ :: The content type to use to encode the form submission,
- # which should be application/x-www-form-urlencoded or
- # multipart/form-data.
- # +formopt+ :: An options hash, supporting the following options:
- # :boundary :: The boundary of the multipart message. If
- # not given, a random boundary will be used.
- # :charset :: The charset of the form submission. All
- # field names and values of non-file fields
- # should be encoded with this charset.
- #
- # Each item of params should respond to +each+ and yield 2-3 arguments,
- # or an array of 2-3 elements. The arguments yielded should be:
- # * The name of the field.
- # * The value of the field, it should be a String or a File or IO-like.
- # * An options hash, supporting the following options, only
- # used for file uploads:
- # :filename :: The name of the file to use.
- # :content_type :: The content type of the uploaded file.
- #
- # Each item is a file field or a normal field.
- # If +value+ is a File object or the +opt+ hash has a :filename key,
- # the item is treated as a file field.
- #
- # If Transfer-Encoding is set as chunked, this sends the request using
- # chunked encoding. Because chunked encoding is HTTP/1.1 feature,
- # you should confirm that the server supports HTTP/1.1 before using
- # chunked encoding.
- #
- # Example:
- # req.set_form([["q", "ruby"], ["lang", "en"]])
- #
- # req.set_form({"f"=>File.open('/path/to/filename')},
- # "multipart/form-data",
- # charset: "UTF-8",
- # )
- #
- # req.set_form([["f",
- # File.open('/path/to/filename.bar'),
- # {filename: "other-filename.foo"}
- # ]],
- # "multipart/form-data",
- # )
- #
- # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5
+ # Stores form data to be used in a +POST+ or +PUT+ request.
+ #
+ # The form data given in +params+ consists of zero or more fields;
+ # each field is:
+ #
+ # - A scalar value.
+ # - A name/value pair.
+ # - An IO stream opened for reading.
+ #
+ # Argument +params+ should be an
+ # {Enumerable}[rdoc-ref:Enumerable@Enumerable+in+Ruby+Classes]
+ # (method <tt>params.map</tt> will be called),
+ # and is often an array or hash.
+ #
+ # First, we set up a request:
+ #
+ # _uri = uri.dup
+ # _uri.path ='/posts'
+ # req = Net::HTTP::Post.new(_uri)
+ #
+ # <b>Argument +params+ As an Array</b>
+ #
+ # When +params+ is an array,
+ # each of its elements is a subarray that defines a field;
+ # the subarray may contain:
+ #
+ # - One string:
+ #
+ # req.set_form([['foo'], ['bar'], ['baz']])
+ #
+ # - Two strings:
+ #
+ # req.set_form([%w[foo 0], %w[bar 1], %w[baz 2]])
+ #
+ # - When argument +enctype+ (see below) is given as
+ # <tt>'multipart/form-data'</tt>:
+ #
+ # - A string name and an IO stream opened for reading:
+ #
+ # require 'stringio'
+ # req.set_form([['file', StringIO.new('Ruby is cool.')]])
+ #
+ # - A string name, an IO stream opened for reading,
+ # and an options hash, which may contain these entries:
+ #
+ # - +:filename+: The name of the file to use.
+ # - +:content_type+: The content type of the uploaded file.
+ #
+ # Example:
+ #
+ # req.set_form([['file', file, {filename: "other-filename.foo"}]]
+ #
+ # The various forms may be mixed:
+ #
+ # req.set_form(['foo', %w[bar 1], ['file', file]])
+ #
+ # <b>Argument +params+ As a Hash</b>
+ #
+ # When +params+ is a hash,
+ # each of its entries is a name/value pair that defines a field:
+ #
+ # - The name is a string.
+ # - The value may be:
+ #
+ # - +nil+.
+ # - Another string.
+ # - An IO stream opened for reading
+ # (only when argument +enctype+ -- see below -- is given as
+ # <tt>'multipart/form-data'</tt>).
+ #
+ # Examples:
+ #
+ # # Nil-valued fields.
+ # req.set_form({'foo' => nil, 'bar' => nil, 'baz' => nil})
+ #
+ # # String-valued fields.
+ # req.set_form({'foo' => 0, 'bar' => 1, 'baz' => 2})
+ #
+ # # IO-valued field.
+ # require 'stringio'
+ # req.set_form({'file' => StringIO.new('Ruby is cool.')})
+ #
+ # # Mixture of fields.
+ # req.set_form({'foo' => nil, 'bar' => 1, 'file' => file})
+ #
+ # Optional argument +enctype+ specifies the value to be given
+ # to field <tt>'Content-Type'</tt>, and must be one of:
+ #
+ # - <tt>'application/x-www-form-urlencoded'</tt> (the default).
+ # - <tt>'multipart/form-data'</tt>;
+ # see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578].
+ #
+ # Optional argument +formopt+ is a hash of options
+ # (applicable only when argument +enctype+
+ # is <tt>'multipart/form-data'</tt>)
+ # that may include the following entries:
+ #
+ # - +:boundary+: The value is the boundary string for the multipart message.
+ # If not given, the boundary is a random string.
+ # See {Boundary}[https://www.rfc-editor.org/rfc/rfc7578#section-4.1].
+ # - +:charset+: Value is the character set for the form submission.
+ # Field names and values of non-file fields should be encoded with this charset.
#
def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})
@body_data = params
@@ -485,12 +935,24 @@ module Net::HTTPHeader
end
end
- # Set the Authorization: header for "Basic" authorization.
+ # Sets header <tt>'Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.basic_auth('my_account', 'my_password')
+ # req['Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
def basic_auth(account, password)
@header['authorization'] = [basic_encode(account, password)]
end
- # Set Proxy-Authorization: header for "Basic" authorization.
+ # Sets header <tt>'Proxy-Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.proxy_basic_auth('my_account', 'my_password')
+ # req['Proxy-Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
def proxy_basic_auth(account, password)
@header['proxy-authorization'] = [basic_encode(account, password)]
end
@@ -500,6 +962,7 @@ module Net::HTTPHeader
end
private :basic_encode
+# Returns whether the HTTP session is to be closed.
def connection_close?
token = /(?:\A|,)\s*close\s*(?:\z|,)/i
@header['connection']&.grep(token) {return true}
@@ -507,6 +970,7 @@ module Net::HTTPHeader
false
end
+# Returns whether the HTTP session is to be kept alive.
def connection_keep_alive?
token = /(?:\A|,)\s*keep-alive\s*(?:\z|,)/i
@header['connection']&.grep(token) {return true}
diff --git a/lib/net/http/net-http.gemspec b/lib/net/http/net-http.gemspec
index a7f122fc0e..0021136793 100644
--- a/lib/net/http/net-http.gemspec
+++ b/lib/net/http/net-http.gemspec
@@ -2,9 +2,14 @@
name = File.basename(__FILE__, ".gemspec")
version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
+ file = File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")
+ begin
+ break File.foreach(file, mode: "rb") do |line|
+ /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
+ end
+ rescue SystemCallError
+ next
+ end
end
Gem::Specification.new do |spec|
@@ -25,7 +30,7 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{\A(?:(?:test|spec|features)/|\.git)}) }
end
spec.bindir = "exe"
spec.require_paths = ["lib"]
diff --git a/lib/net/http/proxy_delta.rb b/lib/net/http/proxy_delta.rb
index a2f770ebdb..e7d30def64 100644
--- a/lib/net/http/proxy_delta.rb
+++ b/lib/net/http/proxy_delta.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net::HTTP::ProxyDelta #:nodoc: internal use only
private
diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb
index 1e86f3e4b4..4a138572e9 100644
--- a/lib/net/http/request.rb
+++ b/lib/net/http/request.rb
@@ -1,8 +1,76 @@
-# frozen_string_literal: false
-# HTTP request class.
-# This class wraps together the request header and the request path.
-# You cannot use this class directly. Instead, you should use one of its
-# subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head.
+# frozen_string_literal: true
+
+# This class is the base class for \Net::HTTP request classes.
+# The class should not be used directly;
+# instead you should use its subclasses, listed below.
+#
+# == Creating a Request
+#
+# An request object may be created with either a URI or a string hostname:
+#
+# require 'net/http'
+# uri = URI('https://jsonplaceholder.typicode.com/')
+# req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+# req = Net::HTTP::Get.new(uri.hostname) # => #<Net::HTTP::Get GET>
+#
+# And with any of the subclasses:
+#
+# req = Net::HTTP::Head.new(uri) # => #<Net::HTTP::Head HEAD>
+# req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+# req = Net::HTTP::Put.new(uri) # => #<Net::HTTP::Put PUT>
+# # ...
+#
+# The new instance is suitable for use as the argument to Net::HTTP#request.
+#
+# == Request Headers
+#
+# A new request object has these header fields by default:
+#
+# req.to_hash
+# # =>
+# {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+# "accept"=>["*/*"],
+# "user-agent"=>["Ruby"],
+# "host"=>["jsonplaceholder.typicode.com"]}
+#
+# See:
+#
+# - {Request header Accept-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Accept-Encoding]
+# and {Compression and Decompression}[rdoc-ref:Net::HTTP@Compression+and+Decompression].
+# - {Request header Accept}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#accept-request-header].
+# - {Request header User-Agent}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#user-agent-request-header].
+# - {Request header Host}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#host-request-header].
+#
+# You can add headers or override default headers:
+#
+# # res = Net::HTTP::Get.new(uri, {'foo' => '0', 'bar' => '1'})
+#
+# This class (and therefore its subclasses) also includes (indirectly)
+# module Net::HTTPHeader, which gives access to its
+# {methods for setting headers}[rdoc-ref:Net::HTTPHeader@Setters].
+#
+# == Request Subclasses
+#
+# Subclasses for HTTP requests:
+#
+# - Net::HTTP::Get
+# - Net::HTTP::Head
+# - Net::HTTP::Post
+# - Net::HTTP::Put
+# - Net::HTTP::Delete
+# - Net::HTTP::Options
+# - Net::HTTP::Trace
+# - Net::HTTP::Patch
+#
+# Subclasses for WebDAV requests:
+#
+# - Net::HTTP::Propfind
+# - Net::HTTP::Proppatch
+# - Net::HTTP::Mkcol
+# - Net::HTTP::Copy
+# - Net::HTTP::Move
+# - Net::HTTP::Lock
+# - Net::HTTP::Unlock
#
class Net::HTTPRequest < Net::HTTPGenericRequest
# Creates an HTTP request object for +path+.
@@ -18,4 +86,3 @@ class Net::HTTPRequest < Net::HTTPGenericRequest
path, initheader
end
end
-
diff --git a/lib/net/http/requests.rb b/lib/net/http/requests.rb
index d4c80a3812..5724164205 100644
--- a/lib/net/http/requests.rb
+++ b/lib/net/http/requests.rb
@@ -1,67 +1,257 @@
-# frozen_string_literal: false
-#
+# frozen_string_literal: true
+
# HTTP/1.1 methods --- RFC2616
-#
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method GET}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#GET_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP.get: sends +GET+ request, returns response body.
+# - Net::HTTP#get: sends +GET+ request, returns response object.
+#
class Net::HTTP::Get < Net::HTTPRequest
METHOD = 'GET'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method HEAD}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#HEAD_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Head.new(uri) # => #<Net::HTTP::Head HEAD>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: no.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP#head: sends +HEAD+ request, returns response object.
+#
class Net::HTTP::Head < Net::HTTPRequest
METHOD = 'HEAD'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = false
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method POST}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#POST_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Net::HTTP.post: sends +POST+ request, returns response object.
+# - Net::HTTP#post: sends +POST+ request, returns response object.
+#
class Net::HTTP::Post < Net::HTTPRequest
METHOD = 'POST'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method PUT}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PUT_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Put.new(uri) # => #<Net::HTTP::Put PUT>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
class Net::HTTP::Put < Net::HTTPRequest
METHOD = 'PUT'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
-# See Net::HTTP for usage examples.
+# \Class for representing
+# {HTTP method DELETE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#DELETE_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts/1'
+# req = Net::HTTP::Delete.new(uri) # => #<Net::HTTP::Delete DELETE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#delete: sends +DELETE+ request, returns response object.
+#
class Net::HTTP::Delete < Net::HTTPRequest
METHOD = 'DELETE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {HTTP method OPTIONS}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#OPTIONS_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Options.new(uri) # => #<Net::HTTP::Options OPTIONS>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#options: sends +OPTIONS+ request, returns response object.
+#
class Net::HTTP::Options < Net::HTTPRequest
METHOD = 'OPTIONS'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {HTTP method TRACE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#TRACE_method]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Trace.new(uri) # => #<Net::HTTP::Trace TRACE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: no.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#trace: sends +TRACE+ request, returns response object.
+#
class Net::HTTP::Trace < Net::HTTPRequest
METHOD = 'TRACE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
+# \Class for representing
+# {HTTP method PATCH}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PATCH_method]:
#
-# PATCH method --- RFC5789
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Net::HTTP::Patch.new(uri) # => #<Net::HTTP::Patch PATCH>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Net::HTTP#patch: sends +PATCH+ request, returns response object.
#
-
-# See Net::HTTPGenericRequest for attributes and methods.
class Net::HTTP::Patch < Net::HTTPRequest
METHOD = 'PATCH'
REQUEST_HAS_BODY = true
@@ -72,49 +262,161 @@ end
# WebDAV methods --- RFC2518
#
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method PROPFIND}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Propfind.new(uri) # => #<Net::HTTP::Propfind PROPFIND>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#propfind: sends +PROPFIND+ request, returns response object.
+#
class Net::HTTP::Propfind < Net::HTTPRequest
METHOD = 'PROPFIND'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method PROPPATCH}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPPATCH]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Proppatch.new(uri) # => #<Net::HTTP::Proppatch PROPPATCH>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object.
+#
class Net::HTTP::Proppatch < Net::HTTPRequest
METHOD = 'PROPPATCH'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method MKCOL}[http://www.webdav.org/specs/rfc4918.html#METHOD_MKCOL]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Mkcol.new(uri) # => #<Net::HTTP::Mkcol MKCOL>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#mkcol: sends +MKCOL+ request, returns response object.
+#
class Net::HTTP::Mkcol < Net::HTTPRequest
METHOD = 'MKCOL'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method COPY}[http://www.webdav.org/specs/rfc4918.html#METHOD_COPY]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Copy.new(uri) # => #<Net::HTTP::Copy COPY>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#copy: sends +COPY+ request, returns response object.
+#
class Net::HTTP::Copy < Net::HTTPRequest
METHOD = 'COPY'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method MOVE}[http://www.webdav.org/specs/rfc4918.html#METHOD_MOVE]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Move.new(uri) # => #<Net::HTTP::Move MOVE>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#move: sends +MOVE+ request, returns response object.
+#
class Net::HTTP::Move < Net::HTTPRequest
METHOD = 'MOVE'
REQUEST_HAS_BODY = false
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method LOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_LOCK]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Lock.new(uri) # => #<Net::HTTP::Lock LOCK>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#lock: sends +LOCK+ request, returns response object.
+#
class Net::HTTP::Lock < Net::HTTPRequest
METHOD = 'LOCK'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
-# See Net::HTTPGenericRequest for attributes and methods.
+# \Class for representing
+# {WebDAV method UNLOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK]:
+#
+# require 'net/http'
+# uri = URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Net::HTTP::Unlock.new(uri) # => #<Net::HTTP::Unlock UNLOCK>
+# res = Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Net::HTTP#unlock: sends +UNLOCK+ request, returns response object.
+#
class Net::HTTP::Unlock < Net::HTTPRequest
METHOD = 'UNLOCK'
REQUEST_HAS_BODY = true
diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb
index f8b522f1ff..40de963868 100644
--- a/lib/net/http/response.rb
+++ b/lib/net/http/response.rb
@@ -1,20 +1,136 @@
-# frozen_string_literal: false
-# HTTP response class.
+# frozen_string_literal: true
+
+# This class is the base class for \Net::HTTP response classes.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Returned Responses
+#
+# \Method Net::HTTP.get_response returns
+# an instance of one of the subclasses of \Net::HTTPResponse:
+#
+# Net::HTTP.get_response(uri)
+# # => #<Net::HTTPOK 200 OK readbody=true>
+# Net::HTTP.get_response(hostname, '/nosuch')
+# # => #<Net::HTTPNotFound 404 Not Found readbody=true>
+#
+# As does method Net::HTTP#request:
+#
+# req = Net::HTTP::Get.new(uri)
+# Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end # => #<Net::HTTPOK 200 OK readbody=true>
+#
+# \Class \Net::HTTPResponse includes module Net::HTTPHeader,
+# which provides access to response header values via (among others):
+#
+# - \Hash-like method <tt>[]</tt>.
+# - Specific reader methods, such as +content_type+.
+#
+# Examples:
+#
+# res = Net::HTTP.get_response(uri) # => #<Net::HTTPOK 200 OK readbody=true>
+# res['Content-Type'] # => "text/html; charset=UTF-8"
+# res.content_type # => "text/html"
+#
+# == Response Subclasses
+#
+# \Class \Net::HTTPResponse has a subclass for each
+# {HTTP status code}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes].
+# You can look up the response class for a given code:
+#
+# Net::HTTPResponse::CODE_TO_OBJ['200'] # => Net::HTTPOK
+# Net::HTTPResponse::CODE_TO_OBJ['400'] # => Net::HTTPBadRequest
+# Net::HTTPResponse::CODE_TO_OBJ['404'] # => Net::HTTPNotFound
+#
+# And you can retrieve the status code for a response object:
+#
+# Net::HTTP.get_response(uri).code # => "200"
+# Net::HTTP.get_response(hostname, '/nosuch').code # => "404"
+#
+# The response subclasses (indentation shows class hierarchy):
#
-# This class wraps together the response header and the response body (the
-# entity requested).
+# - Net::HTTPUnknownResponse (for unhandled \HTTP extensions).
#
-# It mixes in the HTTPHeader module, which provides access to response
-# header values both via hash-like methods and via individual readers.
+# - Net::HTTPInformation:
#
-# Note that each possible HTTP response code defines its own
-# HTTPResponse subclass. All classes are defined under the Net module.
-# Indentation indicates inheritance. For a list of the classes see Net::HTTP.
+# - Net::HTTPContinue (100)
+# - Net::HTTPSwitchProtocol (101)
+# - Net::HTTPProcessing (102)
+# - Net::HTTPEarlyHints (103)
#
-# Correspondence <code>HTTP code => class</code> is stored in CODE_TO_OBJ
-# constant:
+# - Net::HTTPSuccess:
#
-# Net::HTTPResponse::CODE_TO_OBJ['404'] #=> Net::HTTPNotFound
+# - Net::HTTPOK (200)
+# - Net::HTTPCreated (201)
+# - Net::HTTPAccepted (202)
+# - Net::HTTPNonAuthoritativeInformation (203)
+# - Net::HTTPNoContent (204)
+# - Net::HTTPResetContent (205)
+# - Net::HTTPPartialContent (206)
+# - Net::HTTPMultiStatus (207)
+# - Net::HTTPAlreadyReported (208)
+# - Net::HTTPIMUsed (226)
+#
+# - Net::HTTPRedirection:
+#
+# - Net::HTTPMultipleChoices (300)
+# - Net::HTTPMovedPermanently (301)
+# - Net::HTTPFound (302)
+# - Net::HTTPSeeOther (303)
+# - Net::HTTPNotModified (304)
+# - Net::HTTPUseProxy (305)
+# - Net::HTTPTemporaryRedirect (307)
+# - Net::HTTPPermanentRedirect (308)
+#
+# - Net::HTTPClientError:
+#
+# - Net::HTTPBadRequest (400)
+# - Net::HTTPUnauthorized (401)
+# - Net::HTTPPaymentRequired (402)
+# - Net::HTTPForbidden (403)
+# - Net::HTTPNotFound (404)
+# - Net::HTTPMethodNotAllowed (405)
+# - Net::HTTPNotAcceptable (406)
+# - Net::HTTPProxyAuthenticationRequired (407)
+# - Net::HTTPRequestTimeOut (408)
+# - Net::HTTPConflict (409)
+# - Net::HTTPGone (410)
+# - Net::HTTPLengthRequired (411)
+# - Net::HTTPPreconditionFailed (412)
+# - Net::HTTPRequestEntityTooLarge (413)
+# - Net::HTTPRequestURITooLong (414)
+# - Net::HTTPUnsupportedMediaType (415)
+# - Net::HTTPRequestedRangeNotSatisfiable (416)
+# - Net::HTTPExpectationFailed (417)
+# - Net::HTTPMisdirectedRequest (421)
+# - Net::HTTPUnprocessableEntity (422)
+# - Net::HTTPLocked (423)
+# - Net::HTTPFailedDependency (424)
+# - Net::HTTPUpgradeRequired (426)
+# - Net::HTTPPreconditionRequired (428)
+# - Net::HTTPTooManyRequests (429)
+# - Net::HTTPRequestHeaderFieldsTooLarge (431)
+# - Net::HTTPUnavailableForLegalReasons (451)
+#
+# - Net::HTTPServerError:
+#
+# - Net::HTTPInternalServerError (500)
+# - Net::HTTPNotImplemented (501)
+# - Net::HTTPBadGateway (502)
+# - Net::HTTPServiceUnavailable (503)
+# - Net::HTTPGatewayTimeOut (504)
+# - Net::HTTPVersionNotSupported (505)
+# - Net::HTTPVariantAlsoNegotiates (506)
+# - Net::HTTPInsufficientStorage (507)
+# - Net::HTTPLoopDetected (508)
+# - Net::HTTPNotExtended (510)
+# - Net::HTTPNetworkAuthenticationRequired (511)
+#
+# There is also the Net::HTTPBadResponse exception which is raised when
+# there is a protocol error.
#
class Net::HTTPResponse
class << self
@@ -108,13 +224,32 @@ class Net::HTTPResponse
# Accept-Encoding header from the user.
attr_accessor :decode_content
- # The encoding to use for the response body. If Encoding, use that encoding.
- # If other true value, attempt to detect the appropriate encoding, and use
- # that.
+ # Returns the value set by body_encoding=, or +false+ if none;
+ # see #body_encoding=.
attr_reader :body_encoding
- # Set the encoding to use for the response body. If given a String, find
- # the related Encoding.
+ # Sets the encoding that should be used when reading the body:
+ #
+ # - If the given value is an Encoding object, that encoding will be used.
+ # - Otherwise if the value is a string, the value of
+ # {Encoding#find(value)}[rdoc-ref:Encoding.find]
+ # will be used.
+ # - Otherwise an encoding will be deduced from the body itself.
+ #
+ # Examples:
+ #
+ # http = Net::HTTP.new(hostname)
+ # req = Net::HTTP::Get.new('/')
+ #
+ # http.request(req) do |res|
+ # p res.body.encoding # => #<Encoding:ASCII-8BIT>
+ # end
+ #
+ # http.request(req) do |res|
+ # res.body_encoding = "UTF-8"
+ # p res.body.encoding # => #<Encoding:UTF-8>
+ # end
+ #
def body_encoding=(value)
value = Encoding.find(value) if value.is_a?(String)
@body_encoding = value
@@ -138,7 +273,7 @@ class Net::HTTPResponse
def error! #:nodoc:
message = @code
- message += ' ' + @message.dump if @message
+ message = "#{message} #{@message.dump}" if @message
raise error_type().new(message, self)
end
@@ -231,6 +366,7 @@ class Net::HTTPResponse
@body = nil
end
@read = true
+ return if @body.nil?
case enc = @body_encoding
when Encoding, false, nil
@@ -246,26 +382,26 @@ class Net::HTTPResponse
@body
end
- # Returns the full entity body.
+ # Returns the string response body;
+ # note that repeated calls for the unmodified body return a cached string:
#
- # Calling this method a second or subsequent time will return the
- # string already read.
+ # path = '/todos/1'
+ # Net::HTTP.start(hostname) do |http|
+ # res = http.get(path)
+ # p res.body
+ # p http.head(path).body # No body.
+ # end
#
- # http.request_get('/index.html') {|res|
- # puts res.body
- # }
+ # Output:
#
- # http.request_get('/index.html') {|res|
- # p res.body.object_id # 538149362
- # p res.body.object_id # 538149362
- # }
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
+ # nil
#
def body
read_body()
end
- # Because it may be necessary to modify the body, Eg, decompression
- # this method facilitates that.
+ # Sets the body of the response to the given value.
def body=(value)
@body = value
end
@@ -504,7 +640,7 @@ class Net::HTTPResponse
end
def stream_check
- raise IOError, 'attempt to read body out of block' if @socket.closed?
+ raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed?
end
def procdest(dest, block)
@@ -513,7 +649,7 @@ class Net::HTTPResponse
if block
Net::ReadAdapter.new(block)
else
- dest || ''
+ dest || +''
end
end
diff --git a/lib/net/http/responses.rb b/lib/net/http/responses.rb
index 02a2fdaa4c..6f6fb8d055 100644
--- a/lib/net/http/responses.rb
+++ b/lib/net/http/responses.rb
@@ -3,192 +3,909 @@
# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
module Net
- # :stopdoc:
class HTTPUnknownResponse < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPError #
end
- class HTTPInformation < HTTPResponse # 1xx
+
+ # Parent class for informational (1xx) HTTP response classes.
+ #
+ # An informational response indicates that the request was received and understood.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.1xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_informational_response].
+ #
+ class HTTPInformation < HTTPResponse
HAS_BODY = false
EXCEPTION_TYPE = HTTPError #
end
- class HTTPSuccess < HTTPResponse # 2xx
+
+ # Parent class for success (2xx) HTTP response classes.
+ #
+ # A success response indicates the action requested by the client
+ # was received, understood, and accepted.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.2xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success].
+ #
+ class HTTPSuccess < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPError #
end
- class HTTPRedirection < HTTPResponse # 3xx
+
+ # Parent class for redirection (3xx) HTTP response classes.
+ #
+ # A redirection response indicates the client must take additional action
+ # to complete the request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.3xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_redirection].
+ #
+ class HTTPRedirection < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPRetriableError #
end
- class HTTPClientError < HTTPResponse # 4xx
+
+ # Parent class for client error (4xx) HTTP response classes.
+ #
+ # A client error response indicates that the client may have caused an error.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.4xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors].
+ #
+ class HTTPClientError < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPClientException #
end
- class HTTPServerError < HTTPResponse # 5xx
+
+ # Parent class for server error (5xx) HTTP response classes.
+ #
+ # A server error response indicates that the server failed to fulfill a request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.5xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_server_errors].
+ #
+ class HTTPServerError < HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = HTTPFatalError #
end
- class HTTPContinue < HTTPInformation # 100
+ # Response class for +Continue+ responses (status code 100).
+ #
+ # A +Continue+ response indicates that the server has received the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-100-continue].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#100].
+ #
+ class HTTPContinue < HTTPInformation
HAS_BODY = false
end
- class HTTPSwitchProtocol < HTTPInformation # 101
+
+ # Response class for <tt>Switching Protocol</tt> responses (status code 101).
+ #
+ # The <tt>Switching Protocol<tt> response indicates that the server has received
+ # a request to switch protocols, and has agreed to do so.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#101].
+ #
+ class HTTPSwitchProtocol < HTTPInformation
HAS_BODY = false
end
- class HTTPProcessing < HTTPInformation # 102
+
+ # Response class for +Processing+ responses (status code 102).
+ #
+ # The +Processing+ response indicates that the server has received
+ # and is processing the request, but no response is available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 2518}[https://www.rfc-editor.org/rfc/rfc2518#section-10.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#102].
+ #
+ class HTTPProcessing < HTTPInformation
HAS_BODY = false
end
- class HTTPEarlyHints < HTTPInformation # 103 - RFC 8297
+
+ # Response class for <tt>Early Hints</tt> responses (status code 103).
+ #
+ # The <tt>Early Hints</tt> indicates that the server has received
+ # and is processing the request, and contains certain headers;
+ # the final response is not available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103].
+ # - {RFC 8297}[https://www.rfc-editor.org/rfc/rfc8297.html#section-2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#103].
+ #
+ class HTTPEarlyHints < HTTPInformation
HAS_BODY = false
end
- class HTTPOK < HTTPSuccess # 200
+ # Response class for +OK+ responses (status code 200).
+ #
+ # The +OK+ response indicates that the server has received
+ # a request and has responded successfully.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#200].
+ #
+ class HTTPOK < HTTPSuccess
HAS_BODY = true
end
- class HTTPCreated < HTTPSuccess # 201
+
+ # Response class for +Created+ responses (status code 201).
+ #
+ # The +Created+ response indicates that the server has received
+ # and has fulfilled a request to create a new resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-201-created].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#201].
+ #
+ class HTTPCreated < HTTPSuccess
HAS_BODY = true
end
- class HTTPAccepted < HTTPSuccess # 202
+
+ # Response class for +Accepted+ responses (status code 202).
+ #
+ # The +Accepted+ response indicates that the server has received
+ # and is processing a request, but the processing has not yet been completed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-202-accepted].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#202].
+ #
+ class HTTPAccepted < HTTPSuccess
HAS_BODY = true
end
- class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
+
+ # Response class for <tt>Non-Authoritative Information</tt> responses (status code 203).
+ #
+ # The <tt>Non-Authoritative Information</tt> response indicates that the server
+ # is a transforming proxy (such as a Web accelerator)
+ # that received a 200 OK response from its origin,
+ # and is returning a modified version of the origin's response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/203].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-203-non-authoritative-infor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#203].
+ #
+ class HTTPNonAuthoritativeInformation < HTTPSuccess
HAS_BODY = true
end
- class HTTPNoContent < HTTPSuccess # 204
+
+ # Response class for <tt>No Content</tt> responses (status code 204).
+ #
+ # The <tt>No Content</tt> response indicates that the server
+ # successfully processed the request, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#204].
+ #
+ class HTTPNoContent < HTTPSuccess
HAS_BODY = false
end
- class HTTPResetContent < HTTPSuccess # 205
+
+ # Response class for <tt>Reset Content</tt> responses (status code 205).
+ #
+ # The <tt>Reset Content</tt> response indicates that the server
+ # successfully processed the request,
+ # asks that the client reset its document view, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-205-reset-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#205].
+ #
+ class HTTPResetContent < HTTPSuccess
HAS_BODY = false
end
- class HTTPPartialContent < HTTPSuccess # 206
+
+ # Response class for <tt>Partial Content</tt> responses (status code 206).
+ #
+ # The <tt>Partial Content</tt> response indicates that the server is delivering
+ # only part of the resource (byte serving)
+ # due to a Range header in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-206-partial-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#206].
+ #
+ class HTTPPartialContent < HTTPSuccess
HAS_BODY = true
end
- class HTTPMultiStatus < HTTPSuccess # 207 - RFC 4918
+
+ # Response class for <tt>Multi-Status (WebDAV)</tt> responses (status code 207).
+ #
+ # The <tt>Multi-Status (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the message body can contain a number of separate response codes.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4818}[https://www.rfc-editor.org/rfc/rfc4918#section-11.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#207].
+ #
+ class HTTPMultiStatus < HTTPSuccess
HAS_BODY = true
end
- class HTTPAlreadyReported < HTTPSuccess # 208 - RFC 5842
+
+ # Response class for <tt>Already Reported (WebDAV)</tt> responses (status code 208).
+ #
+ # The <tt>Already Reported (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the members of a DAV binding have already been enumerated
+ # in a preceding part of the (multi-status) response,
+ # and are not being included again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 5842}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#208].
+ #
+ class HTTPAlreadyReported < HTTPSuccess
HAS_BODY = true
end
- class HTTPIMUsed < HTTPSuccess # 226 - RFC 3229
+
+ # Response class for <tt>IM Used</tt> responses (status code 226).
+ #
+ # The <tt>IM Used</tt> response indicates that the server has fulfilled a request
+ # for the resource, and the response is a representation of the result
+ # of one or more instance-manipulations applied to the current instance.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 3229}[https://www.rfc-editor.org/rfc/rfc3229.html#section-10.4.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#226].
+ #
+ class HTTPIMUsed < HTTPSuccess
HAS_BODY = true
end
- class HTTPMultipleChoices < HTTPRedirection # 300
+ # Response class for <tt>Multiple Choices</tt> responses (status code 300).
+ #
+ # The <tt>Multiple Choices</tt> response indicates that the server
+ # offers multiple options for the resource from which the client may choose.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-300-multiple-choices].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#300].
+ #
+ class HTTPMultipleChoices < HTTPRedirection
HAS_BODY = true
end
HTTPMultipleChoice = HTTPMultipleChoices
- class HTTPMovedPermanently < HTTPRedirection # 301
+
+ # Response class for <tt>Moved Permanently</tt> responses (status code 301).
+ #
+ # The <tt>Moved Permanently</tt> response indicates that links or records
+ # returning this response should be updated to use the given URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-301-moved-permanently].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#301].
+ #
+ class HTTPMovedPermanently < HTTPRedirection
HAS_BODY = true
end
- class HTTPFound < HTTPRedirection # 302
+
+ # Response class for <tt>Found</tt> responses (status code 302).
+ #
+ # The <tt>Found</tt> response indicates that the client
+ # should look at (browse to) another URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-302-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#302].
+ #
+ class HTTPFound < HTTPRedirection
HAS_BODY = true
end
HTTPMovedTemporarily = HTTPFound
- class HTTPSeeOther < HTTPRedirection # 303
+
+ # Response class for <tt>See Other</tt> responses (status code 303).
+ #
+ # The response to the request can be found under another URI using the GET method.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-303-see-other].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#303].
+ #
+ class HTTPSeeOther < HTTPRedirection
HAS_BODY = true
end
- class HTTPNotModified < HTTPRedirection # 304
+
+ # Response class for <tt>Not Modified</tt> responses (status code 304).
+ #
+ # Indicates that the resource has not been modified since the version
+ # specified by the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-304-not-modified].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#304].
+ #
+ class HTTPNotModified < HTTPRedirection
HAS_BODY = false
end
- class HTTPUseProxy < HTTPRedirection # 305
+
+ # Response class for <tt>Use Proxy</tt> responses (status code 305).
+ #
+ # The requested resource is available only through a proxy,
+ # whose address is provided in the response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-305-use-proxy].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#305].
+ #
+ class HTTPUseProxy < HTTPRedirection
HAS_BODY = false
end
- # 306 Switch Proxy - no longer unused
- class HTTPTemporaryRedirect < HTTPRedirection # 307
+
+ # Response class for <tt>Temporary Redirect</tt> responses (status code 307).
+ #
+ # The request should be repeated with another URI;
+ # however, future requests should still use the original URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-307-temporary-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#307].
+ #
+ class HTTPTemporaryRedirect < HTTPRedirection
HAS_BODY = true
end
- class HTTPPermanentRedirect < HTTPRedirection # 308
+
+ # Response class for <tt>Permanent Redirect</tt> responses (status code 308).
+ #
+ # This and all future requests should be directed to the given URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-308-permanent-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#308].
+ #
+ class HTTPPermanentRedirect < HTTPRedirection
HAS_BODY = true
end
- class HTTPBadRequest < HTTPClientError # 400
+ # Response class for <tt>Bad Request</tt> responses (status code 400).
+ #
+ # The server cannot or will not process the request due to an apparent client error.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#400].
+ #
+ class HTTPBadRequest < HTTPClientError
HAS_BODY = true
end
- class HTTPUnauthorized < HTTPClientError # 401
+
+ # Response class for <tt>Unauthorized</tt> responses (status code 401).
+ #
+ # Authentication is required, but either was not provided or failed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#401].
+ #
+ class HTTPUnauthorized < HTTPClientError
HAS_BODY = true
end
- class HTTPPaymentRequired < HTTPClientError # 402
+
+ # Response class for <tt>Payment Required</tt> responses (status code 402).
+ #
+ # Reserved for future use.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-402-payment-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#402].
+ #
+ class HTTPPaymentRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPForbidden < HTTPClientError # 403
+
+ # Response class for <tt>Forbidden</tt> responses (status code 403).
+ #
+ # The request contained valid data and was understood by the server,
+ # but the server is refusing action.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#403].
+ #
+ class HTTPForbidden < HTTPClientError
HAS_BODY = true
end
- class HTTPNotFound < HTTPClientError # 404
+
+ # Response class for <tt>Not Found</tt> responses (status code 404).
+ #
+ # The requested resource could not be found but may be available in the future.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#404].
+ #
+ class HTTPNotFound < HTTPClientError
HAS_BODY = true
end
- class HTTPMethodNotAllowed < HTTPClientError # 405
+
+ # Response class for <tt>Method Not Allowed</tt> responses (status code 405).
+ #
+ # The request method is not supported for the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-405-method-not-allowed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#405].
+ #
+ class HTTPMethodNotAllowed < HTTPClientError
HAS_BODY = true
end
- class HTTPNotAcceptable < HTTPClientError # 406
+
+ # Response class for <tt>Not Acceptable</tt> responses (status code 406).
+ #
+ # The requested resource is capable of generating only content
+ # that not acceptable according to the Accept headers sent in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#406].
+ #
+ class HTTPNotAcceptable < HTTPClientError
HAS_BODY = true
end
- class HTTPProxyAuthenticationRequired < HTTPClientError # 407
+
+ # Response class for <tt>Proxy Authentication Required</tt> responses (status code 407).
+ #
+ # The client must first authenticate itself with the proxy.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-407-proxy-authentication-re].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#407].
+ #
+ class HTTPProxyAuthenticationRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPRequestTimeout < HTTPClientError # 408
+
+ # Response class for <tt>Request Timeout</tt> responses (status code 408).
+ #
+ # The server timed out waiting for the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-408-request-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#408].
+ #
+ class HTTPRequestTimeout < HTTPClientError
HAS_BODY = true
end
HTTPRequestTimeOut = HTTPRequestTimeout
- class HTTPConflict < HTTPClientError # 409
+
+ # Response class for <tt>Conflict</tt> responses (status code 409).
+ #
+ # The request could not be processed because of conflict in the current state of the resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#409].
+ #
+ class HTTPConflict < HTTPClientError
HAS_BODY = true
end
- class HTTPGone < HTTPClientError # 410
+
+ # Response class for <tt>Gone</tt> responses (status code 410).
+ #
+ # The resource requested was previously in use but is no longer available
+ # and will not be available again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-410-gone].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#410].
+ #
+ class HTTPGone < HTTPClientError
HAS_BODY = true
end
- class HTTPLengthRequired < HTTPClientError # 411
+
+ # Response class for <tt>Length Required</tt> responses (status code 411).
+ #
+ # The request did not specify the length of its content,
+ # which is required by the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-411-length-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#411].
+ #
+ class HTTPLengthRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPPreconditionFailed < HTTPClientError # 412
+
+ # Response class for <tt>Precondition Failed</tt> responses (status code 412).
+ #
+ # The server does not meet one of the preconditions
+ # specified in the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-412-precondition-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#412].
+ #
+ class HTTPPreconditionFailed < HTTPClientError
HAS_BODY = true
end
- class HTTPPayloadTooLarge < HTTPClientError # 413
+
+ # Response class for <tt>Payload Too Large</tt> responses (status code 413).
+ #
+ # The request is larger than the server is willing or able to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-413-content-too-large].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#413].
+ #
+ class HTTPPayloadTooLarge < HTTPClientError
HAS_BODY = true
end
HTTPRequestEntityTooLarge = HTTPPayloadTooLarge
- class HTTPURITooLong < HTTPClientError # 414
+
+ # Response class for <tt>URI Too Long</tt> responses (status code 414).
+ #
+ # The URI provided was too long for the server to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-414-uri-too-long].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#414].
+ #
+ class HTTPURITooLong < HTTPClientError
HAS_BODY = true
end
HTTPRequestURITooLong = HTTPURITooLong
HTTPRequestURITooLarge = HTTPRequestURITooLong
- class HTTPUnsupportedMediaType < HTTPClientError # 415
+
+ # Response class for <tt>Unsupported Media Type</tt> responses (status code 415).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#415].
+ #
+ class HTTPUnsupportedMediaType < HTTPClientError
HAS_BODY = true
end
- class HTTPRangeNotSatisfiable < HTTPClientError # 416
+
+ # Response class for <tt>Range Not Satisfiable</tt> responses (status code 416).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-416-range-not-satisfiable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#416].
+ #
+ class HTTPRangeNotSatisfiable < HTTPClientError
HAS_BODY = true
end
HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable
- class HTTPExpectationFailed < HTTPClientError # 417
+
+ # Response class for <tt>Expectation Failed</tt> responses (status code 417).
+ #
+ # The server cannot meet the requirements of the Expect request-header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-417-expectation-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#417].
+ #
+ class HTTPExpectationFailed < HTTPClientError
HAS_BODY = true
end
+
# 418 I'm a teapot - RFC 2324; a joke RFC
+ # See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#418.
+
# 420 Enhance Your Calm - Twitter
- class HTTPMisdirectedRequest < HTTPClientError # 421 - RFC 7540
+
+ # Response class for <tt>Misdirected Request</tt> responses (status code 421).
+ #
+ # The request was directed at a server that is not able to produce a response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-421-misdirected-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#421].
+ #
+ class HTTPMisdirectedRequest < HTTPClientError
HAS_BODY = true
end
- class HTTPUnprocessableEntity < HTTPClientError # 422 - RFC 4918
+
+ # Response class for <tt>Unprocessable Entity</tt> responses (status code 422).
+ #
+ # The request was well-formed but had semantic errors.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-422-unprocessable-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#422].
+ #
+ class HTTPUnprocessableEntity < HTTPClientError
HAS_BODY = true
end
- class HTTPLocked < HTTPClientError # 423 - RFC 4918
+
+ # Response class for <tt>Locked (WebDAV)</tt> responses (status code 423).
+ #
+ # The requested resource is locked.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#423].
+ #
+ class HTTPLocked < HTTPClientError
HAS_BODY = true
end
- class HTTPFailedDependency < HTTPClientError # 424 - RFC 4918
+
+ # Response class for <tt>Failed Dependency (WebDAV)</tt> responses (status code 424).
+ #
+ # The request failed because it depended on another request and that request failed.
+ # See {424 Failed Dependency (WebDAV)}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ class HTTPFailedDependency < HTTPClientError
HAS_BODY = true
end
- # 425 Unordered Collection - existed only in draft
- class HTTPUpgradeRequired < HTTPClientError # 426 - RFC 2817
+
+ # 425 Too Early
+ # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#425.
+
+ # Response class for <tt>Upgrade Required</tt> responses (status code 426).
+ #
+ # The client should switch to the protocol given in the Upgrade header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-426-upgrade-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#426].
+ #
+ class HTTPUpgradeRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPPreconditionRequired < HTTPClientError # 428 - RFC 6585
+
+ # Response class for <tt>Precondition Required</tt> responses (status code 428).
+ #
+ # The origin server requires the request to be conditional.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#428].
+ #
+ class HTTPPreconditionRequired < HTTPClientError
HAS_BODY = true
end
- class HTTPTooManyRequests < HTTPClientError # 429 - RFC 6585
+
+ # Response class for <tt>Too Many Requests</tt> responses (status code 429).
+ #
+ # The user has sent too many requests in a given amount of time.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429].
+ #
+ class HTTPTooManyRequests < HTTPClientError
HAS_BODY = true
end
- class HTTPRequestHeaderFieldsTooLarge < HTTPClientError # 431 - RFC 6585
+
+ # Response class for <tt>Request Header Fields Too Large</tt> responses (status code 431).
+ #
+ # An individual header field is too large,
+ # or all the header fields collectively, are too large.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#431].
+ #
+ class HTTPRequestHeaderFieldsTooLarge < HTTPClientError
HAS_BODY = true
end
- class HTTPUnavailableForLegalReasons < HTTPClientError # 451 - RFC 7725
+
+ # Response class for <tt>Unavailable For Legal Reasons</tt> responses (status code 451).
+ #
+ # A server operator has received a legal demand to deny access to a resource or to a set of resources
+ # that includes the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451].
+ # - {RFC 7725}[https://www.rfc-editor.org/rfc/rfc7725.html#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#451].
+ #
+ class HTTPUnavailableForLegalReasons < HTTPClientError
HAS_BODY = true
end
# 444 No Response - Nginx
@@ -196,43 +913,188 @@ module Net
# 450 Blocked by Windows Parental Controls - Microsoft
# 499 Client Closed Request - Nginx
- class HTTPInternalServerError < HTTPServerError # 500
+ # Response class for <tt>Internal Server Error</tt> responses (status code 500).
+ #
+ # An unexpected condition was encountered and no more specific message is suitable.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#500].
+ #
+ class HTTPInternalServerError < HTTPServerError
HAS_BODY = true
end
- class HTTPNotImplemented < HTTPServerError # 501
+
+ # Response class for <tt>Not Implemented</tt> responses (status code 501).
+ #
+ # The server either does not recognize the request method,
+ # or it lacks the ability to fulfil the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-501-not-implemented].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#501].
+ #
+ class HTTPNotImplemented < HTTPServerError
HAS_BODY = true
end
- class HTTPBadGateway < HTTPServerError # 502
+
+ # Response class for <tt>Bad Gateway</tt> responses (status code 502).
+ #
+ # The server was acting as a gateway or proxy
+ # and received an invalid response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-502-bad-gateway].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#502].
+ #
+ class HTTPBadGateway < HTTPServerError
HAS_BODY = true
end
- class HTTPServiceUnavailable < HTTPServerError # 503
+
+ # Response class for <tt>Service Unavailable</tt> responses (status code 503).
+ #
+ # The server cannot handle the request
+ # (because it is overloaded or down for maintenance).
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#503].
+ #
+ class HTTPServiceUnavailable < HTTPServerError
HAS_BODY = true
end
- class HTTPGatewayTimeout < HTTPServerError # 504
+
+ # Response class for <tt>Gateway Timeout</tt> responses (status code 504).
+ #
+ # The server was acting as a gateway or proxy
+ # and did not receive a timely response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-504-gateway-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#504].
+ #
+ class HTTPGatewayTimeout < HTTPServerError
HAS_BODY = true
end
HTTPGatewayTimeOut = HTTPGatewayTimeout
- class HTTPVersionNotSupported < HTTPServerError # 505
+
+ # Response class for <tt>HTTP Version Not Supported</tt> responses (status code 505).
+ #
+ # The server does not support the HTTP version used in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-505-http-version-not-suppor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#505].
+ #
+ class HTTPVersionNotSupported < HTTPServerError
HAS_BODY = true
end
- class HTTPVariantAlsoNegotiates < HTTPServerError # 506
+
+ # Response class for <tt>Variant Also Negotiates</tt> responses (status code 506).
+ #
+ # Transparent content negotiation for the request results in a circular reference.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506].
+ # - {RFC 2295}[https://www.rfc-editor.org/rfc/rfc2295#section-8.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#506].
+ #
+ class HTTPVariantAlsoNegotiates < HTTPServerError
HAS_BODY = true
end
- class HTTPInsufficientStorage < HTTPServerError # 507 - RFC 4918
+
+ # Response class for <tt>Insufficient Storage (WebDAV)</tt> responses (status code 507).
+ #
+ # The server is unable to store the representation needed to complete the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507].
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#507].
+ #
+ class HTTPInsufficientStorage < HTTPServerError
HAS_BODY = true
end
- class HTTPLoopDetected < HTTPServerError # 508 - RFC 5842
+
+ # Response class for <tt>Loop Detected (WebDAV)</tt> responses (status code 508).
+ #
+ # The server detected an infinite loop while processing the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508].
+ # - {RFC 5942}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#508].
+ #
+ class HTTPLoopDetected < HTTPServerError
HAS_BODY = true
end
# 509 Bandwidth Limit Exceeded - Apache bw/limited extension
- class HTTPNotExtended < HTTPServerError # 510 - RFC 2774
+
+ # Response class for <tt>Not Extended</tt> responses (status code 510).
+ #
+ # Further extensions to the request are required for the server to fulfill it.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510].
+ # - {RFC 2774}[https://www.rfc-editor.org/rfc/rfc2774.html#section-7].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#510].
+ #
+ class HTTPNotExtended < HTTPServerError
HAS_BODY = true
end
- class HTTPNetworkAuthenticationRequired < HTTPServerError # 511 - RFC 6585
+
+ # Response class for <tt>Network Authentication Required</tt> responses (status code 511).
+ #
+ # The client needs to authenticate to gain network access.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-6].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#511].
+ #
+ class HTTPNetworkAuthenticationRequired < HTTPServerError
HAS_BODY = true
end
- # :startdoc:
end
class Net::HTTPResponse
diff --git a/lib/net/http/status.rb b/lib/net/http/status.rb
index 8db3f7d9e3..e70b47d9fb 100644
--- a/lib/net/http/status.rb
+++ b/lib/net/http/status.rb
@@ -4,7 +4,7 @@ require_relative '../http'
if $0 == __FILE__
require 'open-uri'
- IO.foreach(__FILE__) do |line|
+ File.foreach(__FILE__) do |line|
puts line
break if line.start_with?('end')
end
@@ -16,7 +16,7 @@ if $0 == __FILE__
next if ['(Unused)', 'Unassigned', 'Description'].include?(mes)
puts " #{code} => '#{mes}',"
end
- puts "}"
+ puts "} # :nodoc:"
end
Net::HTTP::STATUS_CODES = {
@@ -55,15 +55,16 @@ Net::HTTP::STATUS_CODES = {
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
- 413 => 'Payload Too Large',
+ 413 => 'Content Too Large',
414 => 'URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Range Not Satisfiable',
417 => 'Expectation Failed',
421 => 'Misdirected Request',
- 422 => 'Unprocessable Entity',
+ 422 => 'Unprocessable Content',
423 => 'Locked',
424 => 'Failed Dependency',
+ 425 => 'Too Early',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
@@ -78,6 +79,6 @@ Net::HTTP::STATUS_CODES = {
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
- 510 => 'Not Extended',
+ 510 => 'Not Extended (OBSOLETED)',
511 => 'Network Authentication Required',
-}
+} # :nodoc:
diff --git a/lib/net/https.rb b/lib/net/https.rb
index d46721c82a..0f23e1fb13 100644
--- a/lib/net/https.rb
+++ b/lib/net/https.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= net/https -- SSL/TLS enhancement for Net::HTTP.
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 822bc00574..ea0752a971 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -26,7 +26,7 @@ require 'io/wait'
module Net # :nodoc:
class Protocol #:nodoc: internal use only
- VERSION = "0.1.3"
+ VERSION = "0.2.1"
private
def Protocol.protocol_param(name, val)
@@ -120,6 +120,8 @@ module Net # :nodoc:
@continue_timeout = continue_timeout
@debug_output = debug_output
@rbuf = ''.b
+ @rbuf_empty = true
+ @rbuf_offset = 0
end
attr_reader :io
@@ -154,14 +156,15 @@ module Net # :nodoc:
LOG "reading #{len} bytes..."
read_bytes = 0
begin
- while read_bytes + @rbuf.size < len
- s = rbuf_consume(@rbuf.size)
- read_bytes += s.size
- dest << s
+ while read_bytes + rbuf_size < len
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
s = rbuf_consume(len - read_bytes)
- read_bytes += s.size
+ read_bytes += s.bytesize
dest << s
rescue EOFError
raise unless ignore_eof
@@ -175,9 +178,10 @@ module Net # :nodoc:
read_bytes = 0
begin
while true
- s = rbuf_consume(@rbuf.size)
- read_bytes += s.size
- dest << s
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
rescue EOFError
@@ -188,14 +192,16 @@ module Net # :nodoc:
end
def readuntil(terminator, ignore_eof = false)
+ offset = @rbuf_offset
begin
- until idx = @rbuf.index(terminator)
+ until idx = @rbuf.index(terminator, offset)
+ offset = @rbuf.bytesize
rbuf_fill
end
- return rbuf_consume(idx + terminator.size)
+ return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
rescue EOFError
raise unless ignore_eof
- return rbuf_consume(@rbuf.size)
+ return rbuf_consume
end
end
@@ -208,12 +214,16 @@ module Net # :nodoc:
BUFSIZE = 1024 * 16
def rbuf_fill
- tmp = @rbuf.empty? ? @rbuf : nil
+ tmp = @rbuf_empty ? @rbuf : nil
case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
when String
- return if rv.equal?(tmp)
- @rbuf << rv
- rv.clear
+ @rbuf_empty = false
+ if rv.equal?(tmp)
+ @rbuf_offset = 0
+ else
+ @rbuf << rv
+ rv.clear
+ end
return
when :wait_readable
(io = @io.to_io).wait_readable(@read_timeout) or raise Net::ReadTimeout.new(io)
@@ -228,13 +238,40 @@ module Net # :nodoc:
end while true
end
- def rbuf_consume(len)
- if len == @rbuf.size
+ def rbuf_flush
+ if @rbuf_empty
+ @rbuf.clear
+ @rbuf_offset = 0
+ end
+ nil
+ end
+
+ def rbuf_size
+ @rbuf.bytesize - @rbuf_offset
+ end
+
+ def rbuf_consume_all
+ rbuf_consume if rbuf_size > 0
+ end
+
+ def rbuf_consume(len = nil)
+ if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
s = @rbuf
@rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ elsif len.nil?
+ s = @rbuf.byteslice(@rbuf_offset..-1)
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
else
- s = @rbuf.slice!(0, len)
+ s = @rbuf.byteslice(@rbuf_offset, len)
+ @rbuf_offset += len
+ @rbuf_empty = @rbuf_offset == @rbuf.bytesize
+ rbuf_flush
end
+
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
s
end
diff --git a/lib/open-uri.gemspec b/lib/open-uri.gemspec
index 12f10ef316..cad63e4d80 100644
--- a/lib/open-uri.gemspec
+++ b/lib/open-uri.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "open-uri"
- spec.version = "0.2.0"
+ spec.version = "0.3.0"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
spec.metadata["source_code_uri"] = spec.homepage
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A((bin|test|spec|features)/|\.git|[Rr]ake|Gemfile)|\.gemspec\z}) }
end
spec.executables = []
spec.require_paths = ["lib"]
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index cb9c3aa505..93e8cfcdb7 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -99,6 +99,8 @@ module OpenURI
:open_timeout => true,
:ssl_ca_cert => nil,
:ssl_verify_mode => nil,
+ :ssl_min_version => nil,
+ :ssl_max_version => nil,
:ftp_active_mode => false,
:redirect => true,
:encoding => nil,
@@ -298,6 +300,8 @@ module OpenURI
require 'net/https'
http.use_ssl = true
http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER
+ http.min_version = options[:ssl_min_version]
+ http.max_version = options[:ssl_max_version]
store = OpenSSL::X509::Store.new
if options[:ssl_ca_cert]
Array(options[:ssl_ca_cert]).each do |cert|
@@ -353,7 +357,8 @@ module OpenURI
when Net::HTTPMovedPermanently, # 301
Net::HTTPFound, # 302
Net::HTTPSeeOther, # 303
- Net::HTTPTemporaryRedirect # 307
+ Net::HTTPTemporaryRedirect, # 307
+ Net::HTTPPermanentRedirect # 308
begin
loc_uri = URI.parse(resp['location'])
rescue URI::InvalidURIError
@@ -410,6 +415,13 @@ module OpenURI
end
end
+ # :stopdoc:
+ RE_LWS = /[\r\n\t ]+/n
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
+ # :startdoc:
+
# Mixin for holding meta-information.
module Meta
def Meta.init(obj, src=nil) # :nodoc:
@@ -487,13 +499,6 @@ module OpenURI
end
end
- # :stopdoc:
- RE_LWS = /[\r\n\t ]+/n
- RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
- RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
- RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
- # :startdoc:
-
def content_type_parse # :nodoc:
vs = @metas['content-type']
# The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
@@ -698,6 +703,20 @@ module OpenURI
#
# :ssl_verify_mode is used to specify openssl verify mode.
#
+ # [:ssl_min_version]
+ # Synopsis:
+ # :ssl_min_version=>:TLS1_2
+ #
+ # :ssl_min_version option specifies the minimum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#min_version=.
+ #
+ # [:ssl_max_version]
+ # Synopsis:
+ # :ssl_max_version=>:TLS1_2
+ #
+ # :ssl_max_version option specifies the maximum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#max_version=.
+ #
# [:ftp_active_mode]
# Synopsis:
# :ftp_active_mode=>bool
diff --git a/lib/open3/version.rb b/lib/open3/version.rb
index 5a8e84b4ae..b6b6ee2c9c 100644
--- a/lib/open3/version.rb
+++ b/lib/open3/version.rb
@@ -1,3 +1,3 @@
module Open3
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
end
diff --git a/lib/optparse.rb b/lib/optparse.rb
index 1d42c79045..53a4387bd8 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -425,7 +425,7 @@
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class OptionParser
- OptionParser::Version = "0.2.0"
+ OptionParser::Version = "0.3.1"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -1148,6 +1148,7 @@ XXX
@summary_indent = indent
@default_argv = ARGV
@require_exact = false
+ @raise_unknown = true
add_officious
yield self if block_given?
end
@@ -1225,6 +1226,9 @@ XXX
# abbreviated long option as short option).
attr_accessor :require_exact
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
#
# Heading banner preceding summary.
#
@@ -1502,7 +1506,7 @@ XXX
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
- desc.push(o)
+ desc.push(o) if o && !o.empty?
end
end
@@ -1639,9 +1643,11 @@ XXX
begin
sw, = complete(:long, opt, true)
if require_exact && !sw.long.include?(arg)
+ throw :terminate, arg unless raise_unknown
raise InvalidOption, arg
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1673,6 +1679,7 @@ XXX
end
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1903,10 +1910,13 @@ XXX
# directory ~/.options, then the basename with '.options' suffix
# under XDG and Haiku standard places.
#
- def load(filename = nil)
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, into: nil)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options')) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
basename << ".options"
return [
# XDG
@@ -1918,11 +1928,11 @@ XXX
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir)) rescue nil
+ load(File.expand_path(basename, dir), into: into) rescue nil
}
end
begin
- parse(*IO.readlines(filename).each {|s| s.chomp!})
+ parse(*File.readlines(filename, chomp: true), into: into)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
@@ -2074,10 +2084,23 @@ XXX
f |= Regexp::IGNORECASE if /i/ =~ o
f |= Regexp::MULTILINE if /m/ =~ o
f |= Regexp::EXTENDED if /x/ =~ o
- k = o.delete("imx")
- k = nil if k.empty?
+ case o = o.delete("imx")
+ when ""
+ when "u"
+ s = s.encode(Encoding::UTF_8)
+ when "e"
+ s = s.encode(Encoding::EUC_JP)
+ when "s"
+ s = s.encode(Encoding::SJIS)
+ when "n"
+ f |= Regexp::NOENCODING
+ else
+ raise OptionParser::InvalidArgument, "unknown regexp option - #{o}"
+ end
+ else
+ s ||= all
end
- Regexp.new(s || all, f, k)
+ Regexp.new(s, f)
end
#
diff --git a/lib/pp.gemspec b/lib/pp.gemspec
index d4b0be83df..3f08f400c4 100644
--- a/lib/pp.gemspec
+++ b/lib/pp.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "pp"
- spec.version = "0.3.0"
+ spec.version = "0.4.0"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/pp.rb b/lib/pp.rb
index f43356a3df..81551aa116 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -416,6 +416,26 @@ class Struct # :nodoc:
end
end
+class Data # :nodoc:
+ def pretty_print(q) # :nodoc:
+ q.group(1, sprintf("#<data %s", PP.mcall(self, Kernel, :class).name), '>') {
+ q.seplist(PP.mcall(self, Data, :members), lambda { q.text "," }) {|member|
+ q.breakable
+ q.text member.to_s
+ q.text '='
+ q.group(1) {
+ q.breakable ''
+ q.pp public_send(member)
+ }
+ }
+ }
+ end
+
+ def pretty_print_cycle(q) # :nodoc:
+ q.text sprintf("#<data %s:...>", PP.mcall(self, Kernel, :class).name)
+ end
+end if "3.2" <= RUBY_VERSION
+
class Range # :nodoc:
def pretty_print(q) # :nodoc:
q.pp self.begin
diff --git a/lib/pstore.rb b/lib/pstore.rb
index 8d7137aa39..72deaa1017 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -71,7 +71,7 @@ require "digest"
# when the store is created (see PStore.new).
# The objects are stored and retrieved using
# module Marshal, which means that certain objects cannot be added to the store;
-# see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump].
+# see {Marshal::dump}[rdoc-ref:Marshal.dump].
#
# == Entries
#
@@ -79,11 +79,11 @@ require "digest"
# Each entry has a key and a value, just as in a hash:
#
# - Key: as in a hash, the key can be (almost) any object;
-# see {Hash Keys}[https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Keys].
+# see {Hash Keys}[rdoc-ref:Hash@Hash+Keys].
# You may find it convenient to keep it simple by using only
# symbols or strings as keys.
# - Value: the value may be any object that can be marshalled by \Marshal
-# (see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump])
+# (see {Marshal::dump}[rdoc-ref:Marshal.dump])
# and in fact may be a collection
# (e.g., an array, a hash, a set, a range, etc).
# That collection may in turn contain nested objects,
@@ -194,7 +194,7 @@ require "digest"
# end
#
# And recall that you can use
-# {dig methods}[https://docs.ruby-lang.org/en/master/dig_methods_rdoc.html]
+# {dig methods}[rdoc-ref:dig_methods.rdoc]
# in a returned hierarchy of objects.
#
# == Working with the Store
@@ -326,7 +326,7 @@ require "digest"
# end
#
class PStore
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
diff --git a/lib/racc/info.rb b/lib/racc/info.rb
index bb1f100adc..37bff7edba 100644
--- a/lib/racc/info.rb
+++ b/lib/racc/info.rb
@@ -11,7 +11,7 @@
#++
module Racc
- VERSION = '1.6.0'
+ VERSION = '1.6.2'
Version = VERSION
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
end
diff --git a/lib/racc/racc.gemspec b/lib/racc/racc.gemspec
index 7ee706f63d..1095c8f47e 100644
--- a/lib/racc/racc.gemspec
+++ b/lib/racc/racc.gemspec
@@ -20,7 +20,7 @@ Racc is a LALR(1) parser generator.
DESC
s.authors = ["Minero Aoki", "Aaron Patterson"]
s.email = [nil, "aaron@tenderlovemaking.com"]
- s.homepage = "https://i.loveruby.net/en/projects/racc/"
+ s.homepage = "https://github.com/ruby/racc"
s.licenses = ["Ruby", "BSD-2-Clause"]
s.executables = ["racc"]
s.files = [
diff --git a/lib/racc/statetransitiontable.rb b/lib/racc/statetransitiontable.rb
index cae411c98b..d75fa1657a 100644
--- a/lib/racc/statetransitiontable.rb
+++ b/lib/racc/statetransitiontable.rb
@@ -216,7 +216,7 @@ module Racc
end
i = ii
end
- Regexp.compile(map, nil, 'n')
+ Regexp.compile(map, Regexp::NOENCODING)
end
def set_table(entries, dummy, tbl, chk, ptr)
diff --git a/lib/random/formatter.rb b/lib/random/formatter.rb
index 744853a4b7..4dea61c16c 100644
--- a/lib/random/formatter.rb
+++ b/lib/random/formatter.rb
@@ -1,9 +1,14 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: true
-# == Random number formatter.
+# == \Random number formatter.
#
-# Formats generated random numbers in many manners.
+# Formats generated random numbers in many manners. When <tt>'random/formatter'</tt>
+# is required, several methods are added to empty core module <tt>Random::Formatter</tt>,
+# making them available as Random's instance and module methods.
+#
+# Standard library SecureRandom is also extended with the module, and the methods
+# described below are available as a module methods in it.
#
# === Examples
#
@@ -11,34 +16,45 @@
#
# require 'random/formatter'
#
+# prng = Random.new
# prng.hex(10) #=> "52750b30ffbc7de3b362"
# prng.hex(10) #=> "92b15d6c8dc4beb5f559"
# prng.hex(13) #=> "39b290146bea6ce975c37cfc23"
+# # or just
+# Random.hex #=> "1aed0c631e41be7f77365415541052ee"
#
# Generate random base64 strings:
#
# prng.base64(10) #=> "EcmTPZwWRAozdA=="
# prng.base64(10) #=> "KO1nIU+p9DKxGg=="
# prng.base64(12) #=> "7kJSM/MzBJI+75j8"
+# Random.base64(4) #=> "bsQ3fQ=="
#
# Generate random binary strings:
#
# prng.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# prng.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+# Random.random_bytes(6) #=> "\xA1\xE6Lr\xC43"
#
# Generate alphanumeric strings:
#
# prng.alphanumeric(10) #=> "S8baxMJnPl"
# prng.alphanumeric(10) #=> "aOxAg8BAJe"
+# Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
#
# Generate UUIDs:
#
# prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+# Random.uuid #=> "f14e0271-de96-45cc-8911-8910292a42cd"
+#
+# All methods are available in the standard library SecureRandom, too:
+#
+# SecureRandom.hex #=> "05b45376a30c67238eb93b16499e50cf"
module Random::Formatter
- # Random::Formatter#random_bytes generates a random binary string.
+ # Generate a random binary string.
#
# The argument _n_ specifies the length of the result string.
#
@@ -49,14 +65,16 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # # or
+ # prng = Random.new
# prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
def random_bytes(n=nil)
n = n ? n.to_int : 16
gen_random(n)
end
- # Random::Formatter#hex generates a random hexadecimal string.
+ # Generate a random hexadecimal string.
#
# The argument _n_ specifies the length, in bytes, of the random number to be generated.
# The length of the resulting hexadecimal string is twice of _n_.
@@ -68,13 +86,15 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # # or
+ # prng = Random.new
# prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
def hex(n=nil)
random_bytes(n).unpack1("H*")
end
- # Random::Formatter#base64 generates a random base64 string.
+ # Generate a random base64 string.
#
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
@@ -86,7 +106,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # # or
+ # prng = Random.new
# prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
#
# See RFC 3548 for the definition of base64.
@@ -94,7 +116,7 @@ module Random::Formatter
[random_bytes(n)].pack("m0")
end
- # Random::Formatter#urlsafe_base64 generates a random URL-safe base64 string.
+ # Generate a random URL-safe base64 string.
#
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
@@ -112,7 +134,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # # or
+ # prng = Random.new
# prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
#
# prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
@@ -126,12 +150,14 @@ module Random::Formatter
s
end
- # Random::Formatter#uuid generates a random v4 UUID (Universally Unique IDentifier).
+ # Generate a random v4 UUID (Universally Unique IDentifier).
#
# require 'random/formatter'
#
- # prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
- # prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+ # Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # # or
+ # prng = Random.new
# prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
#
# The version 4 UUID is purely random (except the version).
@@ -139,7 +165,7 @@ module Random::Formatter
#
# The result contains 122 random bits (15.25 random bytes).
#
- # See RFC 4122 for details of UUID.
+ # See RFC4122[https://datatracker.ietf.org/doc/html/rfc4122] for details of UUID.
#
def uuid
ary = random_bytes(16).unpack("NnnnnN")
@@ -152,7 +178,7 @@ module Random::Formatter
self.bytes(n)
end
- # Random::Formatter#choose generates a string that randomly draws from a
+ # Generate a string that randomly draws from a
# source array of characters.
#
# The argument _source_ specifies the array of characters from which
@@ -196,7 +222,7 @@ module Random::Formatter
end
ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
- # Random::Formatter#alphanumeric generates a random alphanumeric string.
+ # Generate a random alphanumeric string.
#
# The argument _n_ specifies the length, in characters, of the alphanumeric
# string to be generated.
@@ -208,7 +234,9 @@ module Random::Formatter
#
# require 'random/formatter'
#
- # prng.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # Random.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # # or
+ # prng = Random.new
# prng.alphanumeric(10) #=> "i6K93NdqiH"
def alphanumeric(n=nil)
n = 16 if n.nil?
diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb
index 41e132450d..b54265717c 100644
--- a/lib/rdoc/generator/markup.rb
+++ b/lib/rdoc/generator/markup.rb
@@ -109,7 +109,7 @@ class RDoc::MethodAttr
lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment
lines.each do |line|
if line =~ /^ *(?=\S)/
- n = $&.length
+ n = $~.end(0)
indent = n if n < indent
break if n == 0
end
diff --git a/lib/rdoc/generator/template/darkfish/_head.rhtml b/lib/rdoc/generator/template/darkfish/_head.rhtml
index 4f331245c3..d5aed3e9ef 100644
--- a/lib/rdoc/generator/template/darkfish/_head.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_head.rhtml
@@ -3,18 +3,18 @@
<title><%= h @title %></title>
<script type="text/javascript">
- var rdoc_rel_prefix = "<%= asset_rel_prefix %>/";
- var index_rel_prefix = "<%= rel_prefix %>/";
+ var rdoc_rel_prefix = "<%= h asset_rel_prefix %>/";
+ var index_rel_prefix = "<%= h rel_prefix %>/";
</script>
-<script src="<%= asset_rel_prefix %>/js/navigation.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/search.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/search_index.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/searcher.js" defer></script>
-<script src="<%= asset_rel_prefix %>/js/darkfish.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/navigation.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/search.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/search_index.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/searcher.js" defer></script>
+<script src="<%= h asset_rel_prefix %>/js/darkfish.js" defer></script>
-<link href="<%= asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
-<link href="<%= asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
<%- @options.template_stylesheets.each do |stylesheet| -%>
-<link href="<%= asset_rel_prefix %>/<%= File.basename stylesheet %>" rel="stylesheet">
+<link href="<%= h asset_rel_prefix %>/<%= File.basename stylesheet %>" rel="stylesheet">
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
index 0ed683ca14..3f68f0c0dc 100644
--- a/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
@@ -12,18 +12,18 @@
<%- end.each do |n, files| -%>
<%- f = files.shift -%>
<%- if files.empty? -%>
- <li><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
+ <li><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h f.page_name %></a>
<%- next -%>
<%- end -%>
<li><details<% if dir == n %> open<% end %>><summary><%
if n == f.page_name
- %><a href="<%= rel_prefix %>/<%= f.path %>"><%= h n %></a><%
+ %><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h n %></a><%
else
%><%= h n %><% files.unshift(f)
end %></summary>
<ul class="link-list">
<%- files.each do |f| -%>
- <li><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
+ <li><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h f.page_name %></a>
<%- end -%>
</ul></details>
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
index bf70819f64..b1e047b5f7 100644
--- a/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
@@ -3,16 +3,37 @@
else
current.comment
end
- table = current.parse(comment).table_of_contents
+ table = current.parse(comment).table_of_contents.dup
if table.length > 1 then %>
<div class="nav-section">
<h3>Table of Contents</h3>
+ <%- display_link = proc do |heading| -%>
+ <a href="#<%= heading.label current %>"><%= heading.plain_html %></a>
+ <%- end -%>
+
+ <%- list_siblings = proc do -%>
+ <%- level = table.first&.level -%>
+ <%- while table.first && table.first.level >= level -%>
+ <%- heading = table.shift -%>
+ <%- if table.first.nil? || table.first.level <= heading.level -%>
+ <li><% display_link.call heading -%>
+ <%- else -%>
+ <li>
+ <details open>
+ <summary><%- display_link.call heading -%></summary>
+ <ul class="link-list" role="directory">
+ <% list_siblings.call %>
+ </ul>
+ </details>
+ </li>
+ <%- end -%>
+ <%- end -%>
+ <%- end -%>
+
<ul class="link-list" role="directory">
-<%- table.each do |heading| -%>
- <li><a href="#<%= heading.label current %>"><%= heading.plain_html %></a>
-<%- end -%>
+ <% list_siblings.call %>
</ul>
</div>
<%- end -%>
diff --git a/lib/rdoc/generator/template/darkfish/class.rhtml b/lib/rdoc/generator/template/darkfish/class.rhtml
index 5d7b6a1b80..97d175dddc 100644
--- a/lib/rdoc/generator/template/darkfish/class.rhtml
+++ b/lib/rdoc/generator/template/darkfish/class.rhtml
@@ -98,28 +98,30 @@
<%- methods.each do |method| -%>
<div id="<%= method.aref %>" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
- <%- if (call_seq = method.call_seq) then -%>
- <%- call_seq.strip.split("\n").each_with_index do |call_seq, i| -%>
- <div class="method-heading">
- <span class="method-callseq">
- <%= h(call_seq.strip.
- gsub( /^\w+\./m, '')).
- gsub(/(.*)[-=]&gt;/, '\1&rarr;') %>
- </span>
- <%- if i == 0 and method.token_stream then -%>
- <span class="method-click-advice">click to toggle source</span>
- <%- end -%>
- </div>
- <%- end -%>
- <%- else -%>
- <div class="method-heading">
- <span class="method-name"><%= h method.name %></span><span
- class="method-args"><%= h method.param_seq %></span>
- <%- if method.token_stream then -%>
- <span class="method-click-advice">click to toggle source</span>
+ <div class="method-header">
+ <%- if (call_seq = method.call_seq) then -%>
+ <%- call_seq.strip.split("\n").each_with_index do |call_seq, i| -%>
+ <div class="method-heading">
+ <span class="method-callseq">
+ <%= h(call_seq.strip.
+ gsub( /^\w+\./m, '')).
+ gsub(/(.*)[-=]&gt;/, '\1&rarr;') %>
+ </span>
+ <%- if i == 0 and method.token_stream then -%>
+ <span class="method-click-advice">click to toggle source</span>
+ <%- end -%>
+ </div>
+ <%- end -%>
+ <%- else -%>
+ <div class="method-heading">
+ <span class="method-name"><%= h method.name %></span><span
+ class="method-args"><%= h method.param_seq %></span>
+ <%- if method.token_stream then -%>
+ <span class="method-click-advice">click to toggle source</span>
+ <%- end -%>
+ </div>
<%- end -%>
</div>
- <%- end -%>
<div class="method-description">
<%- if method.comment then -%>
diff --git a/lib/rdoc/generator/template/darkfish/index.rhtml b/lib/rdoc/generator/template/darkfish/index.rhtml
index 13fa3dcc7f..423e225b68 100644
--- a/lib/rdoc/generator/template/darkfish/index.rhtml
+++ b/lib/rdoc/generator/template/darkfish/index.rhtml
@@ -17,6 +17,6 @@
main_page = @files.find { |f| f.full_name == @options.main_page } then %>
<%= main_page.description %>
<%- else -%>
-<p>This is the API documentation for <%= @title %>.
+<p>This is the API documentation for <%= h @title %>.
<%- end -%>
</main>
diff --git a/lib/rdoc/generator/template/darkfish/js/darkfish.js b/lib/rdoc/generator/template/darkfish/js/darkfish.js
index 111bbf8eb9..d0c9467751 100644
--- a/lib/rdoc/generator/template/darkfish/js/darkfish.js
+++ b/lib/rdoc/generator/template/darkfish/js/darkfish.js
@@ -54,7 +54,7 @@ function hookSearch() {
var html = '';
// TODO add relative path to <script> per-page
- html += '<p class="search-match"><a href="' + index_rel_prefix + result.path + '">' + this.hlt(result.title);
+ html += '<p class="search-match"><a href="' + index_rel_prefix + this.escapeHTML(result.path) + '">' + this.hlt(result.title);
if (result.params)
html += '<span class="params">' + result.params + '</span>';
html += '</a>';
diff --git a/lib/rdoc/generator/template/darkfish/js/search.js b/lib/rdoc/generator/template/darkfish/js/search.js
index b558ca5b4f..58e52afecf 100644
--- a/lib/rdoc/generator/template/darkfish/js/search.js
+++ b/lib/rdoc/generator/template/darkfish/js/search.js
@@ -101,7 +101,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
}
this.escapeHTML = function(html) {
- return html.replace(/[&<>]/g, function(c) {
+ return html.replace(/[&<>"`']/g, function(c) {
return '&#' + c.charCodeAt(0) + ';';
});
}
diff --git a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
index 303d7016cc..941ff9d630 100644
--- a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
+++ b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
@@ -8,14 +8,14 @@
<ul>
<%- simple_files.sort.each do |file| -%>
<li class="file">
- <a href="<%= file.path %>"><%= h file.page_name %></a>
+ <a href="<%= h file.path %>"><%= h file.page_name %></a>
<%
# HACK table_of_contents should not exist on Document
table = file.parse(file.comment).table_of_contents
unless table.empty? then %>
<ul>
<%- table.each do |heading| -%>
- <li><a href="<%= file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a>
+ <li><a href="<%= h file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a>
<%- end -%>
</ul>
<%- end -%>
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
index 26b32f9b73..a0709b6352 100644
--- a/lib/rdoc/markdown.rb
+++ b/lib/rdoc/markdown.rb
@@ -209,45 +209,75 @@ class RDoc::Markdown
attr_accessor :result, :pos
def current_column(target=pos)
- if c = string.rindex("\n", target-1)
- return target - c - 1
+ if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
+ return target - c
+ elsif c = string.rindex("\n", target)
+ return target - c
end
target + 1
end
+ def position_line_offsets
+ unless @position_line_offsets
+ @position_line_offsets = []
+ total = 0
+ string.each_line do |line|
+ total += line.size
+ @position_line_offsets << total
+ end
+ end
+ @position_line_offsets
+ end
+
if [].respond_to? :bsearch_index
def current_line(target=pos)
- unless @line_offsets
- @line_offsets = []
- total = 0
- string.each_line do |line|
- total += line.size
- @line_offsets << total
- end
+ if line = position_line_offsets.bsearch_index {|x| x > target }
+ return line + 1
end
-
- @line_offsets.bsearch_index {|x| x >= target } + 1 || -1
+ raise "Target position #{target} is outside of string"
end
else
def current_line(target=pos)
- cur_offset = 0
- cur_line = 0
-
- string.each_line do |line|
- cur_line += 1
- cur_offset += line.size
- return cur_line if cur_offset >= target
+ if line = position_line_offsets.index {|x| x > target }
+ return line + 1
end
- -1
+ raise "Target position #{target} is outside of string"
end
end
+ def current_character(target=pos)
+ if target < 0 || target >= string.size
+ raise "Target position #{target} is outside of string"
+ end
+ string[target, 1]
+ end
+
+ KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
+
+ def current_pos_info(target=pos)
+ l = current_line target
+ c = current_column target
+ ln = get_line(l-1)
+ chr = string[target,1]
+ KpegPosInfo.new(target, l, c, ln, chr)
+ end
+
def lines
- lines = []
- string.each_line { |l| lines << l }
- lines
+ string.lines
+ end
+
+ def get_line(no)
+ loff = position_line_offsets
+ if no < 0
+ raise "Line No is out of range: #{no} < 0"
+ elsif no >= loff.size
+ raise "Line No is out of range: #{no} >= #{loff.size}"
+ end
+ lend = loff[no]-1
+ lstart = no > 0 ? loff[no-1] : 0
+ string[lstart..lend]
end
@@ -261,6 +291,7 @@ class RDoc::Markdown
@string = string
@string_size = string ? string.size : 0
@pos = pos
+ @position_line_offsets = nil
end
def show_pos
@@ -285,30 +316,22 @@ class RDoc::Markdown
end
def failure_caret
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- line = lines[l-1]
- "#{line}\n#{' ' * (c - 1)}^"
+ p = current_pos_info @failing_rule_offset
+ "#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
end
def failure_character
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
- lines[l-1][c-1, 1]
+ current_character @failing_rule_offset
end
def failure_oneline
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- char = lines[l-1][c-1, 1]
+ p = current_pos_info @failing_rule_offset
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
- "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
else
- "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
end
end
@@ -321,10 +344,9 @@ class RDoc::Markdown
def show_error(io=STDOUT)
error_pos = @failing_rule_offset
- line_no = current_line(error_pos)
- col_no = current_column(error_pos)
+ p = current_pos_info(error_pos)
- io.puts "On line #{line_no}, column #{col_no}:"
+ io.puts "On line #{p.lno}, column #{p.col}:"
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
@@ -333,10 +355,9 @@ class RDoc::Markdown
io.puts "Failed to match rule '#{@failed_rule}'"
end
- io.puts "Got: #{string[error_pos,1].inspect}"
- line = lines[line_no-1]
- io.puts "=> #{line}"
- io.print(" " * (col_no + 3))
+ io.puts "Got: #{p.char.inspect}"
+ io.puts "=> #{p.line}"
+ io.print(" " * (p.col + 2))
io.puts "^"
end
@@ -445,6 +466,7 @@ class RDoc::Markdown
end
def apply_with_args(rule, *args)
+ @result = nil
memo_key = [rule, args]
if m = @memoizations[memo_key][@pos]
@pos = m.pos
@@ -478,6 +500,7 @@ class RDoc::Markdown
end
def apply(rule)
+ @result = nil
if m = @memoizations[rule][@pos]
@pos = m.pos
if !m.set
@@ -15910,7 +15933,7 @@ class RDoc::Markdown
return _tmp
end
- # Table = &{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }
+ # Table = &{ github? } TableHead:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }
def _Table
_save = self.pos
@@ -15922,7 +15945,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- _tmp = apply(:_TableRow)
+ _tmp = apply(:_TableHead)
header = @result
unless _tmp
self.pos = _save
@@ -15966,18 +15989,18 @@ class RDoc::Markdown
return _tmp
end
- # TableRow = TableItem+:row "|" @Newline { row }
- def _TableRow
+ # TableHead = TableItem2+:items "|"? @Newline { items }
+ def _TableHead
_save = self.pos
while true # sequence
_save1 = self.pos
_ary = []
- _tmp = apply(:_TableItem)
+ _tmp = apply(:_TableItem2)
if _tmp
_ary << @result
while true
- _tmp = apply(:_TableItem)
+ _tmp = apply(:_TableItem2)
_ary << @result if _tmp
break unless _tmp
end
@@ -15986,13 +16009,18 @@ class RDoc::Markdown
else
self.pos = _save1
end
- row = @result
+ items = @result
unless _tmp
self.pos = _save
break
end
+ _save2 = self.pos
_tmp = match_string("|")
unless _tmp
+ _tmp = true
+ self.pos = _save2
+ end
+ unless _tmp
self.pos = _save
break
end
@@ -16001,7 +16029,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin; row ; end
+ @result = begin; items ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -16009,90 +16037,92 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableRow unless _tmp
+ set_failed_rule :_TableHead unless _tmp
return _tmp
end
- # TableItem = "|" < (!"|" !@Newline .)+ > { text.strip }
- def _TableItem
+ # TableRow = ((TableItem:item1 TableItem2*:items { [item1, *items] }):row | TableItem2+:row) "|"? @Newline { row }
+ def _TableRow
_save = self.pos
while true # sequence
- _tmp = match_string("|")
- unless _tmp
- self.pos = _save
- break
- end
- _text_start = self.pos
+
_save1 = self.pos
+ while true # choice
- _save2 = self.pos
- while true # sequence
- _save3 = self.pos
- _tmp = match_string("|")
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save2
+ _save2 = self.pos
+ while true # sequence
+ _tmp = apply(:_TableItem)
+ item1 = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _ary = []
+ while true
+ _tmp = apply(:_TableItem2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ items = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; [item1, *items] ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
break
- end
+ end # end sequence
+
+ row = @result
+ break if _tmp
+ self.pos = _save1
_save4 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save4
- unless _tmp
- self.pos = _save2
- break
- end
- _tmp = get_byte
- unless _tmp
- self.pos = _save2
+ _ary = []
+ _tmp = apply(:_TableItem2)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_TableItem2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save4
end
+ row = @result
+ break if _tmp
+ self.pos = _save1
break
- end # end sequence
-
- if _tmp
- while true
-
- _save5 = self.pos
- while true # sequence
- _save6 = self.pos
- _tmp = match_string("|")
- _tmp = _tmp ? nil : true
- self.pos = _save6
- unless _tmp
- self.pos = _save5
- break
- end
- _save7 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save7
- unless _tmp
- self.pos = _save5
- break
- end
- _tmp = get_byte
- unless _tmp
- self.pos = _save5
- end
- break
- end # end sequence
+ end # end choice
- break unless _tmp
- end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string("|")
+ unless _tmp
_tmp = true
- else
- self.pos = _save1
+ self.pos = _save5
end
- if _tmp
- text = get_text(_text_start)
+ unless _tmp
+ self.pos = _save
+ break
end
+ _tmp = _Newline()
unless _tmp
self.pos = _save
break
end
- @result = begin; text.strip ; end
+ @result = begin; row ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -16100,46 +16130,46 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableItem unless _tmp
+ set_failed_rule :_TableRow unless _tmp
return _tmp
end
- # TableLine = TableColumn+:line "|" @Newline { line }
- def _TableLine
+ # TableItem2 = "|" TableItem
+ def _TableItem2
_save = self.pos
while true # sequence
- _save1 = self.pos
- _ary = []
- _tmp = apply(:_TableColumn)
- if _tmp
- _ary << @result
- while true
- _tmp = apply(:_TableColumn)
- _ary << @result if _tmp
- break unless _tmp
- end
- _tmp = true
- @result = _ary
- else
- self.pos = _save1
- end
- line = @result
+ _tmp = match_string("|")
unless _tmp
self.pos = _save
break
end
- _tmp = match_string("|")
+ _tmp = apply(:_TableItem)
unless _tmp
self.pos = _save
- break
end
- _tmp = _Newline()
+ break
+ end # end sequence
+
+ set_failed_rule :_TableItem2 unless _tmp
+ return _tmp
+ end
+
+ # TableItem = < /(?:\\.|[^|\n])+/ > { text.strip.gsub(/\\(.)/, '\1') }
+ def _TableItem
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\G(?-mix:(?:\\.|[^|\n])+)/)
+ if _tmp
+ text = get_text(_text_start)
+ end
unless _tmp
self.pos = _save
break
end
- @result = begin; line ; end
+ @result = begin; text.strip.gsub(/\\(.)/, '\1') ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -16147,80 +16177,136 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableLine unless _tmp
+ set_failed_rule :_TableItem unless _tmp
return _tmp
end
- # TableColumn = "|" < ("-"+ ":"? | ":" "-"*) > { text.start_with?(":") ? :left : text.end_with?(":") ? :right : nil }
- def _TableColumn
+ # TableLine = ((TableAlign:align1 TableAlign2*:aligns {[align1, *aligns] }):line | TableAlign2+:line) "|"? @Newline { line }
+ def _TableLine
_save = self.pos
while true # sequence
- _tmp = match_string("|")
- unless _tmp
- self.pos = _save
- break
- end
- _text_start = self.pos
_save1 = self.pos
while true # choice
_save2 = self.pos
while true # sequence
- _save3 = self.pos
- _tmp = match_string("-")
- if _tmp
- while true
- _tmp = match_string("-")
- break unless _tmp
- end
- _tmp = true
- else
- self.pos = _save3
- end
+ _tmp = apply(:_TableAlign)
+ align1 = @result
unless _tmp
self.pos = _save2
break
end
- _save4 = self.pos
- _tmp = match_string(":")
+ _ary = []
+ while true
+ _tmp = apply(:_TableAlign2)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ aligns = @result
unless _tmp
- _tmp = true
- self.pos = _save4
+ self.pos = _save2
+ break
end
+ @result = begin; [align1, *aligns] ; end
+ _tmp = true
unless _tmp
self.pos = _save2
end
break
end # end sequence
+ line = @result
break if _tmp
self.pos = _save1
-
- _save5 = self.pos
- while true # sequence
- _tmp = match_string(":")
- unless _tmp
- self.pos = _save5
- break
- end
+ _save4 = self.pos
+ _ary = []
+ _tmp = apply(:_TableAlign2)
+ if _tmp
+ _ary << @result
while true
- _tmp = match_string("-")
+ _tmp = apply(:_TableAlign2)
+ _ary << @result if _tmp
break unless _tmp
end
_tmp = true
- unless _tmp
- self.pos = _save5
- end
- break
- end # end sequence
-
+ @result = _ary
+ else
+ self.pos = _save4
+ end
+ line = @result
break if _tmp
self.pos = _save1
break
end # end choice
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string("|")
+ unless _tmp
+ _tmp = true
+ self.pos = _save5
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; line ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableLine unless _tmp
+ return _tmp
+ end
+
+ # TableAlign2 = "|" @Sp TableAlign
+ def _TableAlign2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_TableAlign)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableAlign2 unless _tmp
+ return _tmp
+ end
+
+ # TableAlign = < /:?-+:?/ > @Sp { text.start_with?(":") ? (text.end_with?(":") ? :center : :left) : (text.end_with?(":") ? :right : nil) }
+ def _TableAlign
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\G(?-mix::?-+:?)/)
if _tmp
text = get_text(_text_start)
end
@@ -16228,8 +16314,15 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin; text.start_with?(":") ? :left :
- text.end_with?(":") ? :right : nil
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin;
+ text.start_with?(":") ?
+ (text.end_with?(":") ? :center : :left) :
+ (text.end_with?(":") ? :right : nil)
; end
_tmp = true
unless _tmp
@@ -16238,7 +16331,7 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TableColumn unless _tmp
+ set_failed_rule :_TableAlign unless _tmp
return _tmp
end
@@ -16674,11 +16767,14 @@ class RDoc::Markdown
Rules[:_Notes] = rule_info("Notes", "(Note | SkipBlock)*")
Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine !RawNoteReference OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }")
Rules[:_CodeFence] = rule_info("CodeFence", "&{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }")
- Rules[:_Table] = rule_info("Table", "&{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }")
- Rules[:_TableRow] = rule_info("TableRow", "TableItem+:row \"|\" @Newline { row }")
- Rules[:_TableItem] = rule_info("TableItem", "\"|\" < (!\"|\" !@Newline .)+ > { text.strip }")
- Rules[:_TableLine] = rule_info("TableLine", "TableColumn+:line \"|\" @Newline { line }")
- Rules[:_TableColumn] = rule_info("TableColumn", "\"|\" < (\"-\"+ \":\"? | \":\" \"-\"*) > { text.start_with?(\":\") ? :left : text.end_with?(\":\") ? :right : nil }")
+ Rules[:_Table] = rule_info("Table", "&{ github? } TableHead:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }")
+ Rules[:_TableHead] = rule_info("TableHead", "TableItem2+:items \"|\"? @Newline { items }")
+ Rules[:_TableRow] = rule_info("TableRow", "((TableItem:item1 TableItem2*:items { [item1, *items] }):row | TableItem2+:row) \"|\"? @Newline { row }")
+ Rules[:_TableItem2] = rule_info("TableItem2", "\"|\" TableItem")
+ Rules[:_TableItem] = rule_info("TableItem", "< /(?:\\\\.|[^|\\n])+/ > { text.strip.gsub(/\\\\(.)/, '\\1') }")
+ Rules[:_TableLine] = rule_info("TableLine", "((TableAlign:align1 TableAlign2*:aligns {[align1, *aligns] }):line | TableAlign2+:line) \"|\"? @Newline { line }")
+ Rules[:_TableAlign2] = rule_info("TableAlign2", "\"|\" @Sp TableAlign")
+ Rules[:_TableAlign] = rule_info("TableAlign", "< /:?-+:?/ > @Sp { text.start_with?(\":\") ? (text.end_with?(\":\") ? :center : :left) : (text.end_with?(\":\") ? :right : nil) }")
Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }")
Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }")
Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }")
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
index 4c36672de7..37659b7ae0 100644
--- a/lib/rdoc/markdown/literals.rb
+++ b/lib/rdoc/markdown/literals.rb
@@ -39,45 +39,75 @@ class RDoc::Markdown::Literals
attr_accessor :result, :pos
def current_column(target=pos)
- if c = string.rindex("\n", target-1)
- return target - c - 1
+ if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
+ return target - c
+ elsif c = string.rindex("\n", target)
+ return target - c
end
target + 1
end
+ def position_line_offsets
+ unless @position_line_offsets
+ @position_line_offsets = []
+ total = 0
+ string.each_line do |line|
+ total += line.size
+ @position_line_offsets << total
+ end
+ end
+ @position_line_offsets
+ end
+
if [].respond_to? :bsearch_index
def current_line(target=pos)
- unless @line_offsets
- @line_offsets = []
- total = 0
- string.each_line do |line|
- total += line.size
- @line_offsets << total
- end
+ if line = position_line_offsets.bsearch_index {|x| x > target }
+ return line + 1
end
-
- @line_offsets.bsearch_index {|x| x >= target } + 1 || -1
+ raise "Target position #{target} is outside of string"
end
else
def current_line(target=pos)
- cur_offset = 0
- cur_line = 0
-
- string.each_line do |line|
- cur_line += 1
- cur_offset += line.size
- return cur_line if cur_offset >= target
+ if line = position_line_offsets.index {|x| x > target }
+ return line + 1
end
- -1
+ raise "Target position #{target} is outside of string"
+ end
+ end
+
+ def current_character(target=pos)
+ if target < 0 || target >= string.size
+ raise "Target position #{target} is outside of string"
end
+ string[target, 1]
+ end
+
+ KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
+
+ def current_pos_info(target=pos)
+ l = current_line target
+ c = current_column target
+ ln = get_line(l-1)
+ chr = string[target,1]
+ KpegPosInfo.new(target, l, c, ln, chr)
end
def lines
- lines = []
- string.each_line { |l| lines << l }
- lines
+ string.lines
+ end
+
+ def get_line(no)
+ loff = position_line_offsets
+ if no < 0
+ raise "Line No is out of range: #{no} < 0"
+ elsif no >= loff.size
+ raise "Line No is out of range: #{no} >= #{loff.size}"
+ end
+ lend = loff[no]-1
+ lstart = no > 0 ? loff[no-1] : 0
+ string[lstart..lend]
end
@@ -91,6 +121,7 @@ class RDoc::Markdown::Literals
@string = string
@string_size = string ? string.size : 0
@pos = pos
+ @position_line_offsets = nil
end
def show_pos
@@ -115,30 +146,22 @@ class RDoc::Markdown::Literals
end
def failure_caret
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- line = lines[l-1]
- "#{line}\n#{' ' * (c - 1)}^"
+ p = current_pos_info @failing_rule_offset
+ "#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
end
def failure_character
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
- lines[l-1][c-1, 1]
+ current_character @failing_rule_offset
end
def failure_oneline
- l = current_line @failing_rule_offset
- c = current_column @failing_rule_offset
-
- char = lines[l-1][c-1, 1]
+ p = current_pos_info @failing_rule_offset
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
- "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
else
- "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ "@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
end
end
@@ -151,10 +174,9 @@ class RDoc::Markdown::Literals
def show_error(io=STDOUT)
error_pos = @failing_rule_offset
- line_no = current_line(error_pos)
- col_no = current_column(error_pos)
+ p = current_pos_info(error_pos)
- io.puts "On line #{line_no}, column #{col_no}:"
+ io.puts "On line #{p.lno}, column #{p.col}:"
if @failed_rule.kind_of? Symbol
info = self.class::Rules[@failed_rule]
@@ -163,10 +185,9 @@ class RDoc::Markdown::Literals
io.puts "Failed to match rule '#{@failed_rule}'"
end
- io.puts "Got: #{string[error_pos,1].inspect}"
- line = lines[line_no-1]
- io.puts "=> #{line}"
- io.print(" " * (col_no + 3))
+ io.puts "Got: #{p.char.inspect}"
+ io.puts "=> #{p.line}"
+ io.print(" " * (p.col + 2))
io.puts "^"
end
@@ -275,6 +296,7 @@ class RDoc::Markdown::Literals
end
def apply_with_args(rule, *args)
+ @result = nil
memo_key = [rule, args]
if m = @memoizations[memo_key][@pos]
@pos = m.pos
@@ -308,6 +330,7 @@ class RDoc::Markdown::Literals
end
def apply(rule)
+ @result = nil
if m = @memoizations[rule][@pos]
@pos = m.pos
if !m.set
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index 6ef5af8856..601e6bc189 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -2,6 +2,17 @@
##
# Manages changes of attributes in a block of text
+unless MatchData.method_defined?(:match_length)
+ using Module.new {
+ refine(MatchData) {
+ def match_length(nth)
+ b, e = offset(nth)
+ e - b if b
+ end
+ }
+ }
+end
+
class RDoc::Markup::AttributeManager
##
@@ -153,16 +164,17 @@ class RDoc::Markup::AttributeManager
tags = "[#{tags.join("")}](?!#{PROTECT_ATTR})"
all_tags = "[#{@matching_word_pairs.keys.join("")}](?!#{PROTECT_ATTR})"
- re = /(^|\W|#{all_tags})(#{tags})(\2*[#\\]?[\w:#{PROTECT_ATTR}.\/\[\]-]+?\S?)\2(?!\2)(#{all_tags}|\W|$)/
+ re = /(?:^|\W|#{all_tags})\K(#{tags})(\1*[#\\]?[\w:#{PROTECT_ATTR}.\/\[\]-]+?\S?)\1(?!\1)(?=#{all_tags}|\W|$)/
1 while str.gsub!(re) { |orig|
- attr = @matching_word_pairs[$2]
- attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
- if attr_updated
- $1 + NULL * $2.length + $3 + NULL * $2.length + $4
+ a, w = (m = $~).values_at(1, 2)
+ attr = @matching_word_pairs[a]
+ if attrs.set_attrs(m.begin(2), w.length, attr)
+ a = NULL * a.length
else
- $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
+ a = NON_PRINTING_START + a + NON_PRINTING_END
end
+ a + w + a
}
str.delete!(NON_PRINTING_START + NON_PRINTING_END)
end
@@ -173,9 +185,10 @@ class RDoc::Markup::AttributeManager
@word_pair_map.each do |regexp, attr|
next unless exclusive == exclusive?(attr)
1 while str.gsub!(regexp) { |orig|
- updated = attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ w = (m = ($~))[2]
+ updated = attrs.set_attrs(m.begin(2), w.length, attr)
if updated
- NULL * $1.length + $2 + NULL * $3.length
+ NULL * m.match_length(1) + w + NULL * m.match_length(3)
else
orig
end
@@ -194,9 +207,9 @@ class RDoc::Markup::AttributeManager
1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig|
attr = @html_tags[$1.downcase]
- html_length = $1.length + 2
+ html_length = $~.match_length(1) + 2 # "<>".length
seq = NULL * html_length
- attrs.set_attrs($`.length + html_length, $2.length, attr)
+ attrs.set_attrs($~.begin(2), $~.match_length(2), attr)
seq + $2 + seq + NULL
}
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 2bfabc8942..bf323074de 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -84,7 +84,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
def handle_RDOCLINK url # :nodoc:
case url
when /^rdoc-ref:/
- $'
+ CGI.escapeHTML($')
when /^rdoc-label:/
text = $'
@@ -95,13 +95,11 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
else text
end
- gen_url url, text
+ gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
when /^rdoc-image:/
- "<img src=\"#{$'}\">"
- else
- url =~ /\Ardoc-[a-z]+:/
-
- $'
+ %[<img src=\"#{CGI.escapeHTML($')}\">]
+ when /\Ardoc-[a-z]+:/
+ CGI.escapeHTML($')
end
end
@@ -125,7 +123,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# Reference to a local file relative to the output directory.
def handle_regexp_HYPERLINK(target)
- url = target.text
+ url = CGI.escapeHTML(target.text)
gen_url url, url
end
@@ -154,9 +152,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
label = $1
- url = $2
+ url = CGI.escapeHTML($2)
- label = handle_RDOCLINK label if /^rdoc-image:/ =~ label
+ if /^rdoc-image:/ =~ label
+ label = handle_RDOCLINK(label)
+ else
+ label = CGI.escapeHTML(label)
+ end
gen_url url, label
end
@@ -324,7 +326,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
header.zip(aligns) do |text, align|
@res << '<th'
@res << ' align="' << align << '"' if align
- @res << '>' << CGI.escapeHTML(text) << "</th>\n"
+ @res << '>' << to_html(text) << "</th>\n"
end
@res << "</tr>\n</thead>\n<tbody>\n"
body.each do |row|
@@ -332,7 +334,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
row.zip(aligns) do |text, align|
@res << '<td'
@res << ' align="' << align << '"' if align
- @res << '>' << CGI.escapeHTML(text) << "</td>\n"
+ @res << '>' << to_html(text) << "</td>\n"
end
@res << "</tr>\n"
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 3cdf4fd08b..2a9b05625c 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -330,31 +330,14 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
text_len = 20 if text_len < 20
- re = /^(.{0,#{text_len}})[ \n]/
next_prefix = ' ' * @indent
prefix = @prefix || next_prefix
@prefix = nil
- @res << prefix
-
- while text.length > text_len
- if text =~ re then
- @res << $1
- text.slice!(0, $&.length)
- else
- @res << text.slice!(0, text_len)
- end
-
- @res << "\n" << next_prefix
- end
-
- if text.empty? then
- @res.pop
- @res.pop
- else
- @res << text
- @res << "\n"
+ text.scan(/\G(?:([^ \n]{#{text_len}})(?=[^ \n])|(.{1,#{text_len}})(?:[ \n]|\z))/) do
+ @res << prefix << ($1 || $2) << "\n"
+ prefix = next_prefix
end
end
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 55994c9dcc..eed0f6b39b 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -339,6 +339,10 @@ class RDoc::Options
attr_reader :visibility
+ ##
+ # Indicates if files of test suites should be skipped
+ attr_accessor :skip_tests
+
def initialize loaded_options = nil # :nodoc:
init_ivars
override loaded_options if loaded_options
@@ -386,6 +390,7 @@ class RDoc::Options
@write_options = false
@encoding = Encoding::UTF_8
@charset = @encoding.name
+ @skip_tests = true
end
def init_with map # :nodoc:
@@ -778,6 +783,13 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
+ opt.on("--no-skipping-tests", nil,
+ "Don't skip generating documentation for test and spec files") do |value|
+ @skip_tests = false
+ end
+
+ opt.separator nil
+
opt.on("--extension=NEW=OLD", "-E",
"Treat files ending with .new as if they",
"ended with .old. Using '-E cgi=rb' will",
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 7006265b63..3bb6f5d1f2 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -266,6 +266,23 @@ class RDoc::Parser
autoload :RubyTools, "#{__dir__}/parser/ruby_tools"
autoload :Text, "#{__dir__}/parser/text"
+ ##
+ # Normalizes tabs in +body+
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) do
+ b, e = $~.offset(0)
+ ' ' * (tab_width * (e-b) - b % tab_width)
+ end
+ line
+ end.join "\n"
+ else
+ body
+ end
+ end
end
# simple must come first in order to show up last in the parsers list
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index a03e4663e4..5695bf1acb 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -1058,23 +1058,6 @@ class RDoc::Parser::C < RDoc::Parser
end
##
- # Normalizes tabs in +body+
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = @options.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) do
- ' ' * (tab_width * $&.length - $`.length % tab_width)
- end && $~
- line
- end.join "\n"
- else
- body
- end
- end
-
- ##
# Loads the variable map with the given +name+ from the RDoc::Store, if
# present.
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index 3c5f79632c..b74ead65ab 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -164,15 +164,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
def initialize(top_level, file_name, content, options, stats)
super
- if /\t/ =~ content then
- tab_width = @options.tab_width
- content = content.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) {
- ' ' * (tab_width*$&.length - $`.length % tab_width)
- } && $~
- line
- end.join("\n")
- end
+ content = handle_tab_width(content)
@size = 0
@token_listeners = nil
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
index eb7d46925b..6f70622c0b 100644
--- a/lib/rdoc/rd/block_parser.rb
+++ b/lib/rdoc/rd/block_parser.rb
@@ -18,8 +18,6 @@ class BlockParser < Racc::Parser
# :stopdoc:
-TMPFILE = ["rdtmp", $$, 0]
-
MARK_TO_LEVEL = {
'=' => 1,
'==' => 2,
@@ -129,15 +127,19 @@ def next_token # :nodoc:
# non-RD part begin
when /^=begin\s+(\w+)/
part = $1
+=begin # not imported to RDoc
if @in_part # if in non-RD part
@part_content.push(line)
else
@in_part = part if @tree.filter[part] # if filter exists
# p "BEGIN_PART: #{@in_part}" # DEBUG
end
+=end
+ @in_part = part
# non-RD part end
- when /^=end/
+ when /^=end(?:$|[\s\0\C-d\C-z])/
if @in_part # if in non-RD part
+=begin # not imported to RDoc
# p "END_PART: #{@in_part}" # DEBUG
# make Part-in object
part = RDoc::RD::Part.new(@part_content.join(""), @tree, "r")
@@ -148,20 +150,22 @@ def next_token # :nodoc:
if @tree.filter[@in_part].mode == :rd # if output is RD formatted
subtree = parse_subtree(part_out.to_a)
else # if output is target formatted
- basename = TMPFILE.join('.')
- TMPFILE[-1] += 1
- tmpfile = open(@tree.tmp_dir + "/" + basename + ".#{@in_part}", "w")
- tmpfile.print(part_out)
- tmpfile.close
+ basename = Tempfile.create(["rdtmp", ".#{@in_part}"], @tree.tmp_dir) do |tmpfile|
+ tmpfile.print(part_out)
+ File.basename(tmpfile.path)
+ end
subtree = parse_subtree(["=begin\n", "<<< #{basename}\n", "=end\n"])
end
@in_part = nil
return [:SUBTREE, subtree]
+=end
end
else
+=begin # not imported to RDoc
if @in_part # if in non-RD part
@part_content.push(line)
end
+=end
end
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index 400f9b5bc3..2d8a9dea8c 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -36,6 +36,17 @@ class RDoc::RDoc
GENERATORS = {}
##
+ # List of directory names always skipped
+
+ UNCONDITIONALLY_SKIPPED_DIRECTORIES = %w[CVS .svn .git].freeze
+
+ ##
+ # List of directory names skipped if test suites should be skipped
+
+ TEST_SUITE_DIRECTORY_NAMES = %w[spec test].freeze
+
+
+ ##
# Generator instance used for creating output
attr_accessor :generator
@@ -280,7 +291,10 @@ option)
file_list[rel_file_name] = mtime
end
when "directory" then
- next if rel_file_name == "CVS" || rel_file_name == ".svn"
+ next if UNCONDITIONALLY_SKIPPED_DIRECTORIES.include?(rel_file_name)
+
+ basename = File.basename(rel_file_name)
+ next if options.skip_tests && TEST_SUITE_DIRECTORY_NAMES.include?(basename)
created_rid = File.join rel_file_name, "created.rid"
next if File.file? created_rid
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index dd66d532ca..819cff8aa3 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -1,12 +1,6 @@
# frozen_string_literal: true
-require 'abbrev'
require 'optparse'
-begin
- require 'readline'
-rescue LoadError
-end
-
require_relative '../../rdoc'
require_relative 'formatter' # For RubyGems backwards compatibility
@@ -1080,6 +1074,10 @@ or the PAGER environment variable.
def interactive
puts "\nEnter the method name you want to look up."
+ begin
+ require 'readline'
+ rescue LoadError
+ end
if defined? Readline then
Readline.completion_proc = method :complete
puts "You can use tab to autocomplete."
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index 9fc540d317..c793e49ed8 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -556,9 +556,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- File.open cache_path, 'rb' do |io|
- @cache = Marshal.load io
- end
+ @cache = marshal_load(cache_path)
load_enc = @cache[:encoding]
@@ -615,9 +613,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- File.open file, 'rb' do |io|
- Marshal.load io
- end
+ marshal_load(file)
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name)
error.set_backtrace e.backtrace
@@ -630,14 +626,10 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj.parent =
- find_class_or_module(klass_name) || load_class(klass_name) unless
- obj.parent
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name)
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name + method_name)
error.set_backtrace e.backtrace
@@ -650,11 +642,9 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, page_name)
error.set_backtrace e.backtrace
@@ -976,4 +966,21 @@ class RDoc::Store
@unique_modules
end
+ private
+ def marshal_load(file)
+ File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
+ end
+
+ MarshalFilter = proc do |obj|
+ case obj
+ when true, false, nil, Array, Class, Encoding, Hash, Integer, String, Symbol, RDoc::Text
+ else
+ unless obj.class.name.start_with?("RDoc::")
+ raise TypeError, "not permitted class: #{obj.class.name}"
+ end
+ end
+ obj
+ end
+ private_constant :MarshalFilter
+
end
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index 2c52a6b87b..31c1aa0276 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -5,6 +5,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.4.0'
+ VERSION = '6.5.1.1'
end
diff --git a/lib/reline.rb b/lib/reline.rb
index 0487232a0d..7800a281ce 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -46,21 +46,6 @@ module Reline
keyword_init: true
)
- DIALOG_COLOR_APIS = [
- :dialog_default_bg_color,
- :dialog_default_bg_color_sequence,
- :dialog_default_bg_color=,
- :dialog_default_fg_color,
- :dialog_default_fg_color_sequence,
- :dialog_default_fg_color=,
- :dialog_highlight_bg_color,
- :dialog_highlight_bg_color_sequence,
- :dialog_highlight_bg_color=,
- :dialog_highlight_fg_color,
- :dialog_highlight_fg_color_sequence,
- :dialog_highlight_fg_color=
- ]
-
class Core
ATTR_READER_NAMES = %i(
completion_append_character
@@ -87,8 +72,7 @@ module Reline
extend Forwardable
def_delegators :config,
:autocompletion,
- :autocompletion=,
- *DIALOG_COLOR_APIS
+ :autocompletion=
def initialize
self.output = STDOUT
@@ -272,10 +256,10 @@ module Reline
contents: result,
scrollbar: true,
height: 15,
- bg_color: config.dialog_default_bg_color_sequence,
- pointer_bg_color: config.dialog_highlight_bg_color_sequence,
- fg_color: config.dialog_default_fg_color_sequence,
- pointer_fg_color: config.dialog_highlight_fg_color_sequence
+ bg_color: 46,
+ pointer_bg_color: 45,
+ fg_color: 37,
+ pointer_fg_color: 37
)
}
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
@@ -561,7 +545,6 @@ module Reline
def_single_delegators :core, :add_dialog_proc
def_single_delegators :core, :dialog_proc
def_single_delegators :core, :autocompletion, :autocompletion=
- def_single_delegators :core, *DIALOG_COLOR_APIS
def_single_delegators :core, :readmultiline
def_instance_delegators self, :readmultiline
@@ -584,10 +567,6 @@ module Reline
core.filename_quote_characters = ""
core.special_prefixes = ""
core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
- core.dialog_default_bg_color = :cyan
- core.dialog_default_fg_color = :white
- core.dialog_highlight_bg_color = :magenta
- core.dialog_highlight_fg_color = :white
}
end
@@ -601,24 +580,21 @@ module Reline
end
require 'reline/general_io'
-if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- require 'reline/windows'
- if Reline::Windows.msys_tty?
- Reline::IOGate = if ENV['TERM'] == 'dumb'
- Reline::GeneralIO
- else
- require 'reline/ansi'
- Reline::ANSI
- end
+io = Reline::GeneralIO
+unless ENV['TERM'] == 'dumb'
+ case RbConfig::CONFIG['host_os']
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
+ require 'reline/windows'
+ tty = (io = Reline::Windows).msys_tty?
else
- Reline::IOGate = Reline::Windows
+ tty = $stdout.tty?
end
+end
+Reline::IOGate = if tty
+ require 'reline/ansi'
+ Reline::ANSI
else
- Reline::IOGate = if $stdout.isatty
- require 'reline/ansi'
- Reline::ANSI
- else
- Reline::GeneralIO
- end
+ io
end
+
Reline::HISTORY = Reline::History.new(Reline.core.config)
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
index ab147a6185..c40085e50d 100644
--- a/lib/reline/ansi.rb
+++ b/lib/reline/ansi.rb
@@ -150,7 +150,7 @@ class Reline::ANSI
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
Reline.core.line_editor.resize
end
- (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
+ (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
rescue Errno::EIO
# Maybe the I/O has been closed.
nil
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index ffd1765e4f..5ba269258f 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -46,10 +46,6 @@ class Reline::Config
end
attr_accessor :autocompletion
- attr_reader :dialog_default_bg_color_sequence,
- :dialog_default_fg_color_sequence,
- :dialog_highlight_bg_color_sequence,
- :dialog_highlight_fg_color_sequence
def initialize
@additional_key_bindings = {} # from inputrc
@@ -75,10 +71,6 @@ class Reline::Config
@test_mode = false
@autocompletion = false
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
- @dialog_default_bg_color_sequence = nil
- @dialog_highlight_bg_color_sequence = nil
- @dialog_default_fg_color_sequence = nil
- @dialog_highlight_fg_color_sequence = nil
end
def reset
@@ -104,65 +96,6 @@ class Reline::Config
(val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
end
- def dialog_default_bg_color=(color)
- @dialog_default_bg_color_sequence = dialog_color_to_code(:bg, color)
- end
-
- def dialog_default_fg_color=(color)
- @dialog_default_fg_color_sequence = dialog_color_to_code(:fg, color)
- end
-
- def dialog_highlight_bg_color=(color)
- @dialog_highlight_bg_color_sequence = dialog_color_to_code(:bg, color)
- end
-
- def dialog_highlight_fg_color=(color)
- @dialog_highlight_fg_color_sequence = dialog_color_to_code(:fg, color)
- end
-
- def dialog_default_bg_color
- dialog_code_to_color(:bg, @dialog_default_bg_color_sequence)
- end
-
- def dialog_default_fg_color
- dialog_code_to_color(:fg, @dialog_default_fg_color_sequence)
- end
-
- def dialog_highlight_bg_color
- dialog_code_to_color(:bg, @dialog_highlight_bg_color_sequence)
- end
-
- def dialog_highlight_fg_color
- dialog_code_to_color(:fg, @dialog_highlight_fg_color_sequence)
- end
-
- COLORS = [
- :black,
- :red,
- :green,
- :yellow,
- :blue,
- :magenta,
- :cyan,
- :white
- ].freeze
-
- private def dialog_color_to_code(type, color)
- base = type == :bg ? 40 : 30
- c = COLORS.index(color.to_sym)
-
- if c
- base + c
- else
- raise ArgumentError.new("Unknown color: #{color}.\nAvailable colors: #{COLORS.join(", ")}")
- end
- end
-
- private def dialog_code_to_color(type, code)
- base = type == :bg ? 40 : 30
- COLORS[code - base]
- end
-
def keymap
@key_actors[@keymap_label]
end
@@ -395,14 +328,6 @@ class Reline::Config
@vi_ins_mode_string = retrieve_string(value)
when 'emacs-mode-string'
@emacs_mode_string = retrieve_string(value)
- when 'dialog-default-bg-color'
- self.dialog_default_bg_color = value
- when 'dialog-default-fg-color'
- self.dialog_default_fg_color = value
- when 'dialog-highlight-bg-color'
- self.dialog_highlight_bg_color = value
- when 'dialog-highlight-fg-color'
- self.dialog_highlight_fg_color = value
when *VARIABLE_NAMES then
variable_name = :"@#{name.tr(?-, ?_)}"
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb
index 3fafad5c6e..92c76cbba1 100644
--- a/lib/reline/general_io.rb
+++ b/lib/reline/general_io.rb
@@ -57,6 +57,12 @@ class Reline::GeneralIO
Reline::CursorPos.new(1, 1)
end
+ def self.hide_cursor
+ end
+
+ def self.show_cursor
+ end
+
def self.move_cursor_column(val)
end
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index 8d0719ef7c..8153aaba05 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -655,7 +655,10 @@ class Reline::LineEditor
end
private def padding_space_with_escape_sequences(str, width)
- str + (' ' * (width - calculate_width(str, true)))
+ padding_width = width - calculate_width(str, true)
+ # padding_width should be only positive value. But macOS and Alacritty returns negative value.
+ padding_width = 0 if padding_width < 0
+ str + (' ' * padding_width)
end
private def render_each_dialog(dialog, cursor_column)
@@ -758,7 +761,6 @@ class Reline::LineEditor
@output.write @full_block
elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
@output.write @upper_half_block
- str += ''
elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
@output.write @lower_half_block
else
@@ -1428,7 +1430,7 @@ class Reline::LineEditor
if @waiting_operator_proc
if VI_MOTIONS.include?(method_symbol)
old_cursor, old_byte_pointer = @cursor, @byte_pointer
- @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
+ @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
block.(true)
unless @waiting_proc
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
index 1bb1c02f3d..67a3d694bd 100644
--- a/lib/reline/version.rb
+++ b/lib/reline/version.rb
@@ -1,3 +1,3 @@
module Reline
- VERSION = '0.3.1'
+ VERSION = '0.3.2'
end
diff --git a/lib/resolv-replace.gemspec b/lib/resolv-replace.gemspec
index 6bc07dbe10..48f7108a8e 100644
--- a/lib/resolv-replace.gemspec
+++ b/lib/resolv-replace.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "resolv-replace"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/resolv.gemspec b/lib/resolv.gemspec
index c6a0609b51..f221010ab6 100644
--- a/lib/resolv.gemspec
+++ b/lib/resolv.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "resolv"
- spec.version = "0.2.1"
+ spec.version = "0.2.3"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/resolv.rb b/lib/resolv.rb
index 61c9c7d5cf..eaea69bfd5 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -1624,6 +1624,7 @@ class Resolv
prev_index = @index
save_index = nil
d = []
+ size = -1
while true
raise DecodeError.new("limit exceeded") if @limit <= @index
case @data.getbyte(@index)
@@ -1644,7 +1645,10 @@ class Resolv
end
@index = idx
else
- d << self.get_label
+ l = self.get_label
+ d << l
+ size += 1 + l.string.bytesize
+ raise DecodeError.new("name label data exceed 255 octets") if size > 255
end
end
end
diff --git a/lib/ruby_vm/mjit/c_pointer.rb b/lib/ruby_vm/mjit/c_pointer.rb
new file mode 100644
index 0000000000..a92c2140ae
--- /dev/null
+++ b/lib/ruby_vm/mjit/c_pointer.rb
@@ -0,0 +1,329 @@
+module RubyVM::MJIT # :nodoc: all
+ # Every class under this namespace is a pointer. Even if the type is
+ # immediate, it shouldn't be dereferenced until `*` is called.
+ module CPointer
+ # Note: We'd like to avoid alphabetic method names to avoid a conflict
+ # with member methods. to_i and to_s are considered an exception.
+ class Struct
+ # @param name [String]
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => [RubyVM::MJIT::CType::*, Integer, TrueClass] }]
+ def initialize(addr, sizeof, members)
+ @addr = addr
+ @sizeof = sizeof
+ @members = members
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Serialized address for generated code
+ def to_s
+ "0x#{@addr.to_s(16)}"
+ end
+
+ # Pointer diff
+ def -(struct)
+ raise ArgumentError if self.class != struct.class
+ (@addr - struct.to_i) / @sizeof
+ end
+
+ # Primitive API that does no automatic dereference
+ # TODO: remove this?
+ # @param member [Symbol]
+ def [](member)
+ type, offset = @members.fetch(member)
+ type.new(@addr + offset / 8)
+ end
+
+ private
+
+ # @param member [Symbol]
+ # @param value [Object]
+ def []=(member, value)
+ type, offset = @members.fetch(member)
+ type[@addr + offset / 8] = value
+ end
+
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
+ def self.define(sizeof, members)
+ Class.new(self) do
+ # Return the size of this type
+ define_singleton_method(:sizeof) { sizeof }
+
+ define_method(:initialize) do |addr = nil|
+ if addr.nil? # TODO: get rid of this feature later
+ addr = Fiddle.malloc(sizeof)
+ end
+ super(addr, sizeof, members)
+ end
+
+ members.each do |member, (type, offset, to_ruby)|
+ # Intelligent API that does automatic dereference
+ define_method(member) do
+ value = self[member]
+ if value.respond_to?(:*)
+ value = value.*
+ end
+ if to_ruby
+ value = C.to_ruby(value)
+ end
+ value
+ end
+
+ define_method("#{member}=") do |value|
+ self[member] = value
+ end
+ end
+ end
+ end
+ end
+
+ # Note: We'd like to avoid alphabetic method names to avoid a conflict
+ # with member methods. to_i is considered an exception.
+ class Union
+ # @param _name [String] To be used when it starts defining a union pointer class
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def initialize(addr, sizeof, members)
+ @addr = addr
+ @sizeof = sizeof
+ @members = members
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Move addr to access this pointer like an array
+ def +(index)
+ raise ArgumentError unless index.is_a?(Integer)
+ self.class.new(@addr + index * @sizeof)
+ end
+
+ # Pointer diff
+ def -(union)
+ raise ArgumentError if self.class != union.class
+ (@addr - union.instance_variable_get(:@addr)) / @sizeof
+ end
+
+ # @param sizeof [Integer]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def self.define(sizeof, members)
+ Class.new(self) do
+ # Return the size of this type
+ define_singleton_method(:sizeof) { sizeof }
+
+ define_method(:initialize) do |addr|
+ super(addr, sizeof, members)
+ end
+
+ members.each do |member, type|
+ # Intelligent API that does automatic dereference
+ define_method(member) do
+ value = type.new(@addr)
+ if value.respond_to?(:*)
+ value = value.*
+ end
+ value
+ end
+ end
+ end
+ end
+ end
+
+ class Immediate
+ # @param addr [Integer]
+ # @param size [Integer]
+ # @param pack [String]
+ def initialize(addr, size, pack)
+ @addr = addr
+ @size = size
+ @pack = pack
+ end
+
+ # Get a raw address
+ def to_i
+ @addr
+ end
+
+ # Move addr to addess this pointer like an array
+ def +(index)
+ Immediate.new(@addr + index * @size, @size, @pack)
+ end
+
+ # Dereference
+ def *
+ self[0]
+ end
+
+ # Array access
+ def [](index)
+ return nil if @addr == 0
+ Fiddle::Pointer.new(@addr + index * @size)[0, @size].unpack1(@pack)
+ end
+
+ # Array set
+ def []=(index, value)
+ Fiddle::Pointer.new(@addr + index * @size)[0, @size] = [value].pack(@pack)
+ end
+
+ # Serialized address for generated code. Used for embedding things like body->iseq_encoded.
+ def to_s
+ "0x#{Integer(@addr).to_s(16)}"
+ end
+
+ # @param fiddle_type [Integer] Fiddle::TYPE_*
+ def self.define(fiddle_type)
+ size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type)
+ pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type)
+
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, size, pack)
+ end
+
+ define_singleton_method(:size) do
+ size
+ end
+
+ # Type-level []=: Used by struct fields
+ define_singleton_method(:[]=) do |addr, value|
+ Fiddle::Pointer.new(addr)[0, size] = [value].pack(pack)
+ end
+ end
+ end
+ end
+
+ # -Fiddle::TYPE_CHAR Immediate with special handling of true/false
+ class Bool < Immediate.define(-Fiddle::TYPE_CHAR)
+ # Dereference
+ def *
+ return nil if @addr == 0
+ super != 0
+ end
+
+ def self.[]=(addr, value)
+ super(addr, value ? 1 : 0)
+ end
+ end
+
+ class Pointer
+ attr_reader :type
+
+ # @param addr [Integer]
+ # @param type [Class] RubyVM::MJIT::CType::*
+ def initialize(addr, type)
+ @addr = addr
+ @type = type
+ end
+
+ # Move addr to addess this pointer like an array
+ def +(index)
+ raise ArgumentError unless index.is_a?(Integer)
+ Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP, @type)
+ end
+
+ # Dereference
+ def *
+ return nil if dest_addr == 0
+ @type.new(dest_addr)
+ end
+
+ # Array access
+ def [](index)
+ (self + index).*
+ end
+
+ # Array set
+ # @param index [Integer]
+ # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself or an object that return an address with to_i
+ def []=(index, value)
+ Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] =
+ [value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+
+ private
+
+ def dest_addr
+ Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+
+ def self.define(block)
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, block.call)
+ end
+
+ # Type-level []=: Used by struct fields
+ # @param addr [Integer]
+ # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself, or an object that return an address with to_i
+ define_singleton_method(:[]=) do |addr, value|
+ value = value.to_i
+ Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
+ end
+ end
+ end
+ end
+
+ class BitField
+ # @param addr [Integer]
+ # @param width [Integer]
+ # @param offset [Integer]
+ def initialize(addr, width, offset)
+ @addr = addr
+ @width = width
+ @offset = offset
+ end
+
+ # Dereference
+ def *
+ byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack1('c')
+ if @width == 1
+ bit = (1 & (byte >> @offset))
+ bit == 1
+ elsif @width <= 8 && @offset == 0
+ bitmask = @width.times.sum { |i| 1 << i }
+ byte & bitmask
+ else
+ raise NotImplementedError.new("not-implemented bit field access: width=#{@width} offset=#{@offset}")
+ end
+ end
+
+ # @param width [Integer]
+ # @param offset [Integer]
+ def self.define(width, offset)
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, width, offset)
+ end
+ end
+ end
+ end
+
+ # Give a name to a dynamic CPointer class to see it on inspect
+ def self.with_class_name(prefix, name, cache: false, &block)
+ return block.call if name.empty?
+
+ # Use a cached result only if cache: true
+ class_name = "#{prefix}_#{name}"
+ klass =
+ if cache && self.const_defined?(class_name)
+ self.const_get(class_name)
+ else
+ block.call
+ end
+
+ # Give it a name unless it's already defined
+ unless self.const_defined?(class_name)
+ self.const_set(class_name, klass)
+ end
+
+ klass
+ end
+ end
+end
diff --git a/lib/ruby_vm/mjit/c_type.rb b/lib/ruby_vm/mjit/c_type.rb
new file mode 100644
index 0000000000..9c965ad2fb
--- /dev/null
+++ b/lib/ruby_vm/mjit/c_type.rb
@@ -0,0 +1,91 @@
+require 'fiddle'
+require 'fiddle/pack'
+require_relative 'c_pointer'
+
+module RubyVM::MJIT # :nodoc: all
+ module CType
+ module Struct
+ # @param name [String]
+ # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
+ def self.new(name, sizeof, **members)
+ name = members.keys.join('_') if name.empty?
+ CPointer.with_class_name('Struct', name) do
+ CPointer::Struct.define(sizeof, members)
+ end
+ end
+ end
+
+ module Union
+ # @param name [String]
+ # @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
+ def self.new(name, sizeof, **members)
+ name = members.keys.join('_') if name.empty?
+ CPointer.with_class_name('Union', name) do
+ CPointer::Union.define(sizeof, members)
+ end
+ end
+ end
+
+ module Immediate
+ # @param fiddle_type [Integer]
+ def self.new(fiddle_type)
+ name = Fiddle.constants.find do |const|
+ const.start_with?('TYPE_') && Fiddle.const_get(const) == fiddle_type.abs
+ end&.to_s
+ name.delete_prefix!('TYPE_')
+ if fiddle_type.negative?
+ name.prepend('U')
+ end
+ CPointer.with_class_name('Immediate', name, cache: true) do
+ CPointer::Immediate.define(fiddle_type)
+ end
+ end
+
+ # @param type [String]
+ def self.parse(ctype)
+ new(Fiddle::Importer.parse_ctype(ctype))
+ end
+
+ def self.find(size, signed)
+ fiddle_type = TYPE_MAP.fetch(size)
+ fiddle_type = -fiddle_type unless signed
+ new(fiddle_type)
+ end
+
+ TYPE_MAP = Fiddle::PackInfo::SIZE_MAP.map { |type, size| [size, type.abs] }.to_h
+ private_constant :TYPE_MAP
+ end
+
+ module Bool
+ def self.new
+ CPointer::Bool
+ end
+ end
+
+ class Pointer
+ # This takes a block to avoid "stack level too deep" on a cyclic reference
+ # @param block [Proc]
+ def self.new(&block)
+ CPointer.with_class_name('Pointer', block.object_id.to_s) do
+ CPointer::Pointer.define(block)
+ end
+ end
+ end
+
+ module BitField
+ # @param width [Integer]
+ # @param offset [Integer]
+ def self.new(width, offset)
+ CPointer.with_class_name('BitField', "#{offset}_#{width}") do
+ CPointer::BitField.define(width, offset)
+ end
+ end
+ end
+
+ # Types that are referenced but not part of code generation targets
+ Stub = ::Struct.new(:name)
+
+ # Types that it failed to figure out from the header
+ Unknown = Module.new
+ end
+end
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
new file mode 100644
index 0000000000..81022cd0a8
--- /dev/null
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -0,0 +1,952 @@
+# Available variables and macros in JIT-ed function:
+# ec: the first argument of _mjitXXX
+# reg_cfp: the second argument of _mjitXXX
+# GET_CFP(): refers to `reg_cfp`
+# GET_EP(): refers to `reg_cfp->ep`
+# GET_SP(): refers to `reg_cfp->sp`, or `(stack + stack_size)` if local_stack_p
+# GET_SELF(): refers to `cfp_self`
+# GET_LEP(): refers to `VM_EP_LEP(reg_cfp->ep)`
+# EXEC_EC_CFP(): refers to `val = vm_exec(ec, true)` with frame setup
+# CALL_METHOD(): using `GET_CFP()` and `EXEC_EC_CFP()`
+# TOPN(): refers to `reg_cfp->sp`, or `*(stack + (stack_size - num - 1))` if local_stack_p
+# STACK_ADDR_FROM_TOP(): refers to `reg_cfp->sp`, or `stack + (stack_size - num)` if local_stack_p
+# DISPATCH_ORIGINAL_INSN(): expanded in _mjit_compile_insn.erb
+# THROW_EXCEPTION(): specially defined for JIT
+# RESTORE_REGS(): specially defined for `leave`
+class RubyVM::MJIT::Compiler # :nodoc: all
+ C = RubyVM::MJIT.const_get(:C, false)
+ INSNS = RubyVM::MJIT.const_get(:INSNS, false)
+ UNSUPPORTED_INSNS = [
+ :defineclass, # low priority
+ ]
+
+ def initialize = freeze
+
+ # @param iseq [RubyVM::MJIT::CPointer::Struct]
+ # @param funcname [String]
+ # @param id [Integer]
+ # @return [String,NilClass]
+ def compile(iseq, funcname, id)
+ status = C.compile_status.new # not freed for now
+ status.compiled_iseq = iseq.body
+ status.compiled_id = id
+ init_compile_status(status, iseq.body, true) # not freed for now
+ if iseq.body.ci_size > 0 && status.cc_entries_index == -1
+ return nil
+ end
+
+ src = +''
+ if !status.compile_info.disable_send_cache && !status.compile_info.disable_inlining
+ unless precompile_inlinable_iseqs(src, iseq, status)
+ return nil
+ end
+ end
+
+ src << "VALUE\n#{funcname}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n"
+ success = compile_body(src, iseq, status)
+ src << "\n} // end of #{funcname}\n"
+
+ return success ? src : nil
+ rescue Exception => e # should we use rb_rescue in C instead?
+ if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
+ $stderr.puts "MJIT error: #{e.full_message}"
+ end
+ return nil
+ end
+
+ private
+
+ def compile_body(src, iseq, status)
+ status.success = true
+ status.local_stack_p = !iseq.body.catch_except_p
+
+ if status.local_stack_p
+ src << " VALUE stack[#{iseq.body.stack_max}];\n"
+ else
+ src << " VALUE *stack = reg_cfp->sp;\n"
+ end
+
+ unless status.inlined_iseqs.nil? # i.e. compile root
+ src << " static const rb_iseq_t *original_iseq = (const rb_iseq_t *)#{iseq};\n"
+ end
+ src << " static const VALUE *const original_body_iseq = (VALUE *)#{iseq.body.iseq_encoded};\n"
+
+ src << " VALUE cfp_self = reg_cfp->self;\n" # cache self across the method
+ src << "#undef GET_SELF\n"
+ src << "#define GET_SELF() cfp_self\n"
+
+ # Generate merged ivar guards first if needed
+ if !status.compile_info.disable_ivar_cache && using_ivar?(iseq.body)
+ src << " if (UNLIKELY(!RB_TYPE_P(GET_SELF(), T_OBJECT))) {"
+ src << " goto ivar_cancel;\n"
+ src << " }\n"
+ end
+
+ # Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
+ # are not considered since vm_exec doesn't call jit_exec for catch tables.
+ if iseq.body.param.flags.has_opt
+ src << "\n"
+ src << " switch (reg_cfp->pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded) {\n"
+ (0..iseq.body.param.opt_num).each do |i|
+ pc_offset = iseq.body.param.opt_table[i]
+ src << " case #{pc_offset}:\n"
+ src << " goto label_#{pc_offset};\n"
+ end
+ src << " }\n"
+ end
+
+ compile_insns(0, 0, status, iseq.body, src)
+ compile_cancel_handler(src, iseq.body, status)
+ src << "#undef GET_SELF\n"
+ return status.success
+ end
+
+ # Compile one conditional branch. If it has branchXXX insn, this should be
+ # called multiple times for each branch.
+ def compile_insns(stack_size, pos, status, body, src)
+ branch = C.compile_branch.new # not freed for now
+ branch.stack_size = stack_size
+ branch.finish_p = false
+
+ while pos < body.iseq_size && !already_compiled?(status, pos) && !branch.finish_p
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ status.stack_size_for_pos[pos] = branch.stack_size
+
+ src << "\nlabel_#{pos}: /* #{insn.name} */\n"
+ pos = compile_insn(insn, pos, status, body.iseq_encoded + (pos+1), body, branch, src)
+ if status.success && branch.stack_size > body.stack_max
+ if mjit_opts.warnings || mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: JIT stack size (#{branch.stack_size}) exceeded its max size (#{body.stack_max})"
+ end
+ status.success = false
+ end
+ break unless status.success
+ end
+ end
+
+ # Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
+ # b->stack_size and return next position.
+ #
+ # When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
+ # it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
+ # does not have it can be compiled as usual.
+ def compile_insn(insn, pos, status, operands, body, b, src)
+ sp_inc = C.mjit_call_attribute_sp_inc(insn.bin, operands)
+ next_pos = pos + insn.len
+
+ result = compile_insn_entry(insn, b.stack_size, sp_inc, status.local_stack_p, pos, next_pos, insn.len,
+ status.inlined_iseqs.nil?, status, operands, body)
+ if result.nil?
+ if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: Skipped to compile unsupported instruction: #{insn.name}"
+ end
+ status.success = false
+ else
+ result_src, next_pos, finish_p, compile_insns_p = result
+
+ src << result_src
+ b.stack_size += sp_inc
+
+ if finish_p
+ b.finish_p = true
+ end
+ if compile_insns_p
+ if already_compiled?(status, pos + insn.len)
+ src << "goto label_#{pos + insn.len};\n"
+ else
+ compile_insns(b.stack_size, pos + insn.len, status, body, src)
+ end
+ end
+ end
+
+ # If next_pos is already compiled and this branch is not finished yet,
+ # next instruction won't be compiled in C code next and will need `goto`.
+ if !b.finish_p && next_pos < body.iseq_size && already_compiled?(status, next_pos)
+ src << "goto label_#{next_pos};\n"
+
+ # Verify stack size assumption is the same among multiple branches
+ if status.stack_size_for_pos[next_pos] != b.stack_size
+ if mjit_opts.warnings || mjit_opts.verbose > 0
+ $stderr.puts "MJIT warning: JIT stack assumption is not the same between branches (#{status.stack_size_for_pos[next_pos]} != #{b.stack_size})\n"
+ end
+ status.success = false
+ end
+ end
+
+ return next_pos
+ end
+
+ def compile_insn_entry(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, status, operands, body)
+ finish_p = false
+ compile_insns = false
+
+ # TODO: define this outside this method, or at least cache it
+ opt_send_without_block = INSNS.values.find { |i| i.name == :opt_send_without_block }
+ if opt_send_without_block.nil?
+ raise 'opt_send_without_block not found'
+ end
+ send_compatible_opt_insns = INSNS.values.select do |insn|
+ insn.name.start_with?('opt_') && opt_send_without_block.opes == insn.opes &&
+ insn.expr.lines.any? { |l| l.match(/\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/) }
+ end.map(&:name)
+
+ case insn.name
+ when *UNSUPPORTED_INSNS
+ return nil
+ when :opt_send_without_block, :send
+ if src = compile_send(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when *send_compatible_opt_insns
+ if C.has_cache_for_send(captured_cc_entries(status)[call_data_index(C.CALL_DATA.new(operands[0]), body)], insn.bin) &&
+ src = compile_send(opt_send_without_block, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :getinstancevariable, :setinstancevariable
+ if src = compile_ivar(insn.name, stack_size, pos, status, operands, body)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :opt_getconstant_path
+ if src = compile_getconstant_path(stack_size, pos, insn_len, operands, status)
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :invokebuiltin, :opt_invokebuiltin_delegate, :opt_invokebuiltin_delegate_leave
+ if src = compile_invokebuiltin(insn, stack_size, sp_inc, body, operands)
+ if insn.name == :opt_invokebuiltin_delegate_leave
+ src << compile_leave(stack_size, pos, inlined_iseq_p)
+ finish_p = true
+ end
+ return src, next_pos, finish_p, compile_insns
+ end
+ when :leave
+ if stack_size != 1
+ raise "Unexpected JIT stack_size on leave: #{stack_size}"
+ end
+ src = compile_leave(stack_size, pos, inlined_iseq_p)
+ finish_p = true
+ return src, next_pos, finish_p, compile_insns
+ end
+
+ return compile_insn_default(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, operands)
+ end
+
+ # Optimized case of send / opt_send_without_block instructions.
+ def compile_send(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, status, operands, body)
+ # compiler: Use captured cc to avoid race condition
+ cd = C.CALL_DATA.new(operands[0])
+ cd_index = call_data_index(cd, body)
+ captured_cc = captured_cc_entries(status)[cd_index]
+
+ # compiler: Inline send insn where some supported fastpath is used.
+ ci = cd.ci
+ kw_splat = (C.vm_ci_flag(ci) & C.VM_CALL_KW_SPLAT) > 0
+ if !status.compile_info.disable_send_cache && has_valid_method_type?(captured_cc) && (
+ # `CC_SET_FASTPATH(cd->cc, vm_call_cfunc_with_frame, ...)` in `vm_call_cfunc`
+ (vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_CFUNC && !C.rb_splat_or_kwargs_p(ci) && !kw_splat) ||
+ # `CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(...), vm_call_iseq_optimizable_p(...))` in `vm_callee_setup_arg`,
+ # and support only non-VM_CALL_TAILCALL path inside it
+ (vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_ISEQ &&
+ C.fastpath_applied_iseq_p(ci, captured_cc, iseq = def_iseq_ptr(vm_cc_cme(captured_cc).def)) &&
+ (C.vm_ci_flag(ci) & C.VM_CALL_TAILCALL) == 0)
+ )
+ src = +"{\n"
+
+ # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things.
+ src << " const struct rb_callcache *cc = (struct rb_callcache *)#{captured_cc};\n"
+ src << " const rb_callable_method_entry_t *cc_cme = (rb_callable_method_entry_t *)#{vm_cc_cme(captured_cc)};\n"
+ src << " const VALUE recv = stack[#{stack_size + sp_inc - 1}];\n"
+ # If opt_class_of is true, use RBASIC_CLASS instead of CLASS_OF to reduce code size
+ opt_class_of = !maybe_special_const?(captured_cc.klass)
+ src << " if (UNLIKELY(#{opt_class_of ? 'RB_SPECIAL_CONST_P(recv)' : 'false'} || !vm_cc_valid_p(cc, cc_cme, #{opt_class_of ? 'RBASIC_CLASS' : 'CLASS_OF'}(recv)))) {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto send_cancel;\n"
+ src << " }\n"
+
+ # JIT: move sp and pc if necessary
+ pc_moved_p = compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: If ISeq is inlinable, call the inlined method without pushing a frame.
+ if iseq && status.inlined_iseqs && iseq.body.to_i == status.inlined_iseqs[pos]&.to_i
+ src << " {\n"
+ src << " VALUE orig_self = reg_cfp->self;\n"
+ src << " reg_cfp->self = stack[#{stack_size + sp_inc - 1}];\n"
+ src << " stack[#{stack_size + sp_inc - 1}] = _mjit#{status.compiled_id}_inlined_#{pos}(ec, reg_cfp, orig_self, original_iseq);\n"
+ src << " reg_cfp->self = orig_self;\n"
+ src << " }\n"
+ else
+ # JIT: Forked `vm_sendish` (except method_explorer = vm_search_method_wrap) to inline various things
+ src << " {\n"
+ src << " VALUE val;\n"
+ src << " struct rb_calling_info calling;\n"
+ if insn.name == :send
+ src << " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (const struct rb_callinfo *)#{ci}, (rb_iseq_t *)0x#{operands[1].to_s(16)}, FALSE);\n"
+ else
+ src << " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"
+ end
+ src << " calling.kw_splat = #{kw_splat ? 1 : 0};\n"
+ src << " calling.recv = stack[#{stack_size + sp_inc - 1}];\n"
+ src << " calling.argc = #{C.vm_ci_argc(ci)};\n"
+
+ if vm_cc_cme(captured_cc).def.type == C.VM_METHOD_TYPE_CFUNC
+ # TODO: optimize this more
+ src << " calling.ci = (const struct rb_callinfo *)#{ci};\n" # creating local cd here because operand's cd->cc may not be the same as inlined cc.
+ src << " calling.cc = cc;"
+ src << " val = vm_call_cfunc_with_frame(ec, reg_cfp, &calling);\n"
+ else # :iseq
+ # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
+ src << " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, cc_cme, 0, #{iseq.body.param.size}, #{iseq.body.local_table_size});\n"
+ if iseq.body.catch_except_p
+ src << " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"
+ src << " val = vm_exec(ec, true);\n"
+ else
+ src << " if ((val = jit_exec(ec)) == Qundef) {\n"
+ src << " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n" # This is vm_call0_body's code after vm_call_iseq_setup
+ src << " val = vm_exec(ec, false);\n"
+ src << " }\n"
+ end
+ end
+ src << " stack[#{stack_size + sp_inc - 1}] = val;\n"
+ src << " }\n"
+
+ # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size + sp_inc};\n"
+ if !pc_moved_p
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n"
+ end
+ src << " goto cancel;\n"
+ src << " }\n"
+ end
+
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_ivar(insn_name, stack_size, pos, status, operands, body)
+ iv_cache = C.iseq_inline_storage_entry.new(operands[1]).iv_cache
+ dest_shape_id = iv_cache.value >> C.SHAPE_FLAG_SHIFT
+ source_shape_id = parent_shape_id(dest_shape_id)
+ attr_index = iv_cache.value & ((1 << C.SHAPE_FLAG_SHIFT) - 1)
+
+ src = +''
+ if !status.compile_info.disable_ivar_cache && source_shape_id != C.INVALID_SHAPE_ID
+ # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`.
+ # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: prepare vm_getivar/vm_setivar arguments and variables
+ src << "{\n"
+ src << " VALUE obj = GET_SELF();\n" # T_OBJECT guaranteed by compile_body
+ # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar)
+ if insn_name == :setinstancevariable
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ src << " const shape_id_t dest_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ src << " if (dest_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n"
+ src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n"
+ src << " }\n"
+ else
+ src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ if attr_index == 0 # cache hit, but uninitialized iv
+ src << " /* Uninitialized instance variable */\n"
+ src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " stack[#{stack_size}] = Qnil;\n"
+ src << " }\n"
+ else
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n"
+ src << " stack[#{stack_size}] = ROBJECT_IVPTR(obj)[index];\n"
+ src << " }\n"
+ end
+ end
+ src << " else {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto ivar_cancel;\n"
+ src << " }\n"
+ src << "}\n"
+ return src
+ elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && source_shape_id != C.INVALID_SHAPE_ID
+ # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`.
+ # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: prepare vm_getivar's arguments and variables
+ src << "{\n"
+ src << " VALUE obj = GET_SELF();\n"
+ src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n"
+ src << " const uint32_t index = #{attr_index - 1};\n"
+ # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization)
+ src << " struct gen_ivtbl *ivtbl;\n"
+ src << " if (LIKELY(FL_TEST_RAW(GET_SELF(), FL_EXIVAR) && source_shape_id == rb_shape_get_shape_id(obj) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl))) {\n"
+ src << " stack[#{stack_size}] = ivtbl->ivptr[index];\n"
+ src << " }\n"
+ src << " else {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto exivar_cancel;\n"
+ src << " }\n"
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_invokebuiltin(insn, stack_size, sp_inc, body, operands)
+ bf = C.RB_BUILTIN.new(operands[0])
+ if bf.compiler > 0
+ index = (insn.name == :invokebuiltin ? -1 : operands[1])
+ src = +"{\n"
+ src << " VALUE val;\n"
+ C.builtin_compiler(src, bf, index, stack_size, body.builtin_inline_p)
+ src << " stack[#{stack_size + sp_inc - 1}] = val;\n"
+ src << "}\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_leave(stack_size, pos, inlined_iseq_p)
+ src = +''
+ # Skip vm_pop_frame for inlined call
+ unless inlined_iseq_p
+ # Cancel on interrupts to make leave insn leaf
+ src << " if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);\n"
+ src << " }\n"
+ src << " ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(reg_cfp);\n" # vm_pop_frame
+ end
+ src << " return stack[0];\n"
+ end
+
+ def compile_getconstant_path(stack_size, pos, insn_len, operands, status)
+ ice = C.IC.new(operands[0]).entry
+ if !status.compile_info.disable_const_cache && ice
+ # JIT: Inline everything in IC, and cancel the slow path
+ src = +" if (vm_inlined_ic_hit_p(#{ice.flags}, #{ice.value}, (const rb_cref_t *)#{to_addr(ice.ic_cref)}, reg_cfp->ep)) {\n"
+ src << " stack[#{stack_size}] = #{ice.value};\n"
+ src << " }\n"
+ src << " else {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " goto const_cancel;\n"
+ src << " }\n"
+ return src
+ else
+ return nil
+ end
+ end
+
+ def compile_insn_default(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, operands)
+ src = +''
+ finish_p = false
+ compile_insns = false
+
+ # JIT: Declare stack_size to be used in some macro of _mjit_compile_insn_body.erb
+ src << "{\n"
+ if local_stack_p
+ src << " MAYBE_UNUSED(unsigned int) stack_size = #{stack_size};\n"
+ end
+
+ # JIT: Declare variables for operands, popped values and return values
+ insn.declarations.each do |decl|
+ src << " #{decl};\n"
+ end
+
+ # JIT: Set const expressions for `RubyVM::OperandsUnifications` insn
+ insn.preamble.each do |amble|
+ src << "#{amble.sub(/const \S+\s+/, '')}\n"
+ end
+
+ # JIT: Initialize operands
+ insn.opes.each_with_index do |ope, i|
+ src << " #{ope.fetch(:name)} = (#{ope.fetch(:type)})#{operands[i]};\n"
+ # TODO: resurrect comment_id
+ end
+
+ # JIT: Initialize popped values
+ insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ src << " #{pop.fetch(:name)} = stack[#{stack_size - (i + 1)}];\n"
+ end
+
+ # JIT: move sp and pc if necessary
+ pc_moved_p = compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+
+ # JIT: Print insn body in insns.def
+ next_pos = compile_insn_body(src, insn, pos, next_pos, insn_len, local_stack_p, stack_size, sp_inc, operands)
+
+ # JIT: Set return values
+ insn.rets.reverse_each.with_index do |ret, i|
+ # TOPN(n) = ...
+ src << " stack[#{stack_size + sp_inc - (i + 1)}] = #{ret.fetch(:name)};\n"
+ end
+
+ # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+ # leaf insn may not cancel JIT. leaf_without_check_ints is covered in RUBY_VM_CHECK_INTS of _mjit_compile_insn_body.erb.
+ unless insn.always_leaf? || insn.leaf_without_check_ints?
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size + sp_inc};\n"
+ if !pc_moved_p
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n"
+ end
+ src << " goto cancel;\n"
+ src << " }\n"
+ end
+
+ src << "}\n"
+
+ # compiler: If insn has conditional JUMP, the code should go to the branch not targeted by JUMP next.
+ if insn.expr.match?(/if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/)
+ compile_insns = true
+ end
+
+ # compiler: If insn returns (leave) or does longjmp (throw), the branch should no longer be compiled. TODO: create attr for it?
+ if insn.expr.match?(/\sTHROW_EXCEPTION\([^)]+\);/) || insn.expr.match?(/\bvm_pop_frame\(/)
+ finish_p = true
+ end
+
+ return src, next_pos, finish_p, compile_insns
+ end
+
+ def compile_insn_body(src, insn, pos, next_pos, insn_len, local_stack_p, stack_size, sp_inc, operands)
+ # Print a body of insn, but with macro expansion.
+ expand_simple_macros(insn.expr).each_line do |line|
+ # Expand dynamic macro here
+ # TODO: support combination of following macros in the same line
+ case line
+ when /\A\s+RUBY_VM_CHECK_INTS\(ec\);\s+\z/
+ if insn.leaf_without_check_ints? # lazily move PC and optionalize mjit_call_p here
+ src << " if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {\n"
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n" # ADD_PC(INSN_ATTR(width));
+ src << " rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);\n"
+ src << " if (UNLIKELY(!mjit_call_p)) {\n"
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ src << " goto cancel;\n"
+ src << " }\n"
+ src << " }\n"
+ else
+ src << to_cstr(line)
+ end
+ when /\A\s+JUMP\((?<dest>[^)]+)\);\s+\z/
+ dest = Regexp.last_match[:dest]
+ if insn.name == :opt_case_dispatch # special case... TODO: use another macro to avoid checking name
+ hash_offsets = C.rb_hash_values(operands[0]).uniq
+ else_offset = cast_offset(operands[1])
+ base_pos = pos + insn_len
+
+ src << " switch (#{dest}) {\n"
+ hash_offsets.each do |offset|
+ src << " case #{offset}:\n"
+ src << " goto label_#{base_pos + offset};\n"
+ end
+ src << " case #{else_offset}:\n"
+ src << " goto label_#{base_pos + else_offset};\n"
+ src << " }\n"
+ else
+ # Before we `goto` next insn, we need to set return values, especially for getinlinecache
+ insn.rets.reverse_each.with_index do |ret, i|
+ # TOPN(n) = ...
+ src << " stack[#{stack_size + sp_inc - (i + 1)}] = #{ret.fetch(:name)};\n"
+ end
+
+ next_pos = pos + insn_len + cast_offset(operands[0]) # workaround: assuming dest == operands[0]. TODO: avoid relying on it
+ src << " goto label_#{next_pos};\n"
+ end
+ when /\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/
+ # For `opt_xxx`'s fallbacks.
+ if local_stack_p
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ end
+ src << " reg_cfp->pc = original_body_iseq + #{pos};\n"
+ src << " goto cancel;\n"
+ when /\A(?<prefix>.+\b)INSN_LABEL\((?<name>[^)]+)\)(?<suffix>.+)\z/m
+ prefix, name, suffix = Regexp.last_match[:prefix], Regexp.last_match[:name], Regexp.last_match[:suffix]
+ src << "#{prefix}INSN_LABEL(#{name}_#{pos})#{suffix}"
+ else
+ if insn.handles_sp?
+ # If insn.handles_sp? is true, cfp->sp might be changed inside insns (like vm_caller_setup_arg_block)
+ # and thus we need to use cfp->sp, even when local_stack_p is TRUE. When insn.handles_sp? is true,
+ # cfp->sp should be available too because _mjit_compile_pc_and_sp.erb sets it.
+ src << to_cstr(line)
+ else
+ # If local_stack_p is TRUE and insn.handles_sp? is false, stack values are only available in local variables
+ # for stack. So we need to replace those macros if local_stack_p is TRUE here.
+ case line
+ when /\bGET_SP\(\)/
+ # reg_cfp->sp
+ src << to_cstr(line.sub(/\bGET_SP\(\)/, local_stack_p ? '(stack + stack_size)' : 'GET_SP()'))
+ when /\bSTACK_ADDR_FROM_TOP\((?<num>[^)]+)\)/
+ # #define STACK_ADDR_FROM_TOP(n) (GET_SP()-(n))
+ num = Regexp.last_match[:num]
+ src << to_cstr(line.sub(/\bSTACK_ADDR_FROM_TOP\(([^)]+)\)/, local_stack_p ? "(stack + (stack_size - (#{num})))" : "STACK_ADDR_FROM_TOP(#{num})"))
+ when /\bTOPN\((?<num>[^)]+)\)/
+ # #define TOPN(n) (*(GET_SP()-(n)-1))
+ num = Regexp.last_match[:num]
+ src << to_cstr(line.sub(/\bTOPN\(([^)]+)\)/, local_stack_p ? "*(stack + (stack_size - (#{num}) - 1))" : "TOPN(#{num})"))
+ else
+ src << to_cstr(line)
+ end
+ end
+ end
+ end
+ return next_pos
+ end
+
+ def compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos)
+ # JIT: When an insn is leaf, we don't need to Move pc for a catch table on catch_except_p, #caller_locations,
+ # and rb_profile_frames. For check_ints, we lazily move PC when we have interruptions.
+ pc_moved_p = false
+ unless insn.always_leaf? || insn.leaf_without_check_ints?
+ src << " reg_cfp->pc = original_body_iseq + #{next_pos};\n" # ADD_PC(INSN_ATTR(width));
+ pc_moved_p = true
+ end
+
+ # JIT: move sp to use or preserve stack variables
+ if local_stack_p
+ # sp motion is optimized away for `handles_sp? #=> false` case.
+ # Thus sp should be set properly before `goto cancel`.
+ if insn.handles_sp?
+ # JIT-only behavior (pushing JIT's local variables to VM's stack):
+ push_size = -sp_inc + insn.rets.size - insn.pops.size
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{push_size};\n"
+ push_size.times do |i|
+ src << " *(reg_cfp->sp + #{i - push_size}) = stack[#{stack_size - push_size + i}];\n"
+ end
+ end
+ else
+ if insn.handles_sp?
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size - insn.pops.size};\n" # POPN(INSN_ATTR(popn));
+ else
+ src << " reg_cfp->sp = vm_base_ptr(reg_cfp) + #{stack_size};\n"
+ end
+ end
+ return pc_moved_p
+ end
+
+ # Print the block to cancel inlined method call. It's supporting only `opt_send_without_block` for now.
+ def compile_inlined_cancel_handler(src, body, inline_context)
+ src << "\ncancel:\n"
+ src << " rb_mjit_recompile_inlining(original_iseq);\n"
+
+ # Swap pc/sp set on cancel with original pc/sp.
+ src << " const VALUE *current_pc = reg_cfp->pc;\n"
+ src << " VALUE *current_sp = reg_cfp->sp;\n"
+ src << " reg_cfp->pc = orig_pc;\n"
+ src << " reg_cfp->sp = orig_sp;\n\n"
+
+ # Lazily push the current call frame.
+ src << " struct rb_calling_info calling;\n"
+ src << " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n" # assumes `opt_send_without_block`
+ src << " calling.argc = #{inline_context.orig_argc};\n"
+ src << " calling.recv = reg_cfp->self;\n"
+ src << " reg_cfp->self = orig_self;\n"
+ # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
+ src << " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, (const rb_callable_method_entry_t *)#{inline_context.me}, 0, #{inline_context.param_size}, #{inline_context.local_size});\n\n"
+
+ # Start usual cancel from here.
+ src << " reg_cfp = ec->cfp;\n" # work on the new frame
+ src << " reg_cfp->pc = current_pc;\n"
+ src << " reg_cfp->sp = current_sp;\n"
+ (0...body.stack_max).each do |i| # should be always `status->local_stack_p`
+ src << " *(vm_base_ptr(reg_cfp) + #{i}) = stack[#{i}];\n"
+ end
+ # We're not just returning Qundef here so that caller's normal cancel handler can
+ # push back `stack` to `cfp->sp`.
+ src << " return vm_exec(ec, false);\n"
+ end
+
+ # Print the block to cancel JIT execution.
+ def compile_cancel_handler(src, body, status)
+ if status.inlined_iseqs.nil? # the current ISeq is being inlined
+ compile_inlined_cancel_handler(src, body, status.inline_context)
+ return
+ end
+
+ src << "\nsend_cancel:\n"
+ src << " rb_mjit_recompile_send(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nivar_cancel:\n"
+ src << " rb_mjit_recompile_ivar(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nexivar_cancel:\n"
+ src << " rb_mjit_recompile_exivar(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\nconst_cancel:\n"
+ src << " rb_mjit_recompile_const(original_iseq);\n"
+ src << " goto cancel;\n"
+
+ src << "\ncancel:\n"
+ if status.local_stack_p
+ (0...body.stack_max).each do |i|
+ src << " *(vm_base_ptr(reg_cfp) + #{i}) = stack[#{i}];\n"
+ end
+ end
+ src << " return Qundef;\n"
+ end
+
+ def precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
+ child_status = C.compile_status.new # not freed for now
+ child_status.compiled_iseq = status.compiled_iseq
+ child_status.compiled_id = status.compiled_id
+ init_compile_status(child_status, child_iseq.body, false) # not freed for now
+ child_status.inline_context.orig_argc = C.vm_ci_argc(ci)
+ child_status.inline_context.me = vm_cc_cme(cc).to_i
+ child_status.inline_context.param_size = child_iseq.body.param.size
+ child_status.inline_context.local_size = child_iseq.body.local_table_size
+ if child_iseq.body.ci_size > 0 && child_status.cc_entries_index == -1
+ return false
+ end
+
+ src << "ALWAYS_INLINE(static VALUE _mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n"
+ src << "static inline VALUE\n_mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq)\n{\n"
+ src << " const VALUE *orig_pc = reg_cfp->pc;\n"
+ src << " VALUE *orig_sp = reg_cfp->sp;\n"
+
+ success = compile_body(src, child_iseq, child_status)
+
+ src << "\n} /* end of _mjit#{status.compiled_id}_inlined_#{pos} */\n\n"
+
+ return success;
+ end
+
+ def precompile_inlinable_iseqs(src, iseq, status)
+ body = iseq.body
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ if insn.name == :opt_send_without_block || insn.name == :opt_size # `compile_inlined_cancel_handler` supports only `opt_send_without_block`
+ cd = C.CALL_DATA.new(body.iseq_encoded[pos + 1])
+ ci = cd.ci
+ cc = captured_cc_entries(status)[call_data_index(cd, body)] # use copy to avoid race condition
+
+ if (child_iseq = rb_mjit_inlinable_iseq(ci, cc)) != nil
+ status.inlined_iseqs[pos] = child_iseq.body
+
+ if C.mjit_opts.verbose >= 1 # print beforehand because ISeq may be GCed during copy job.
+ child_location = child_iseq.body.location
+ $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{C.rb_iseq_first_lineno(child_iseq)} " \
+ "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{C.rb_iseq_first_lineno(iseq)}"
+ end
+ if !precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
+ return false
+ end
+ end
+ end
+ pos += insn.len
+ end
+ return true
+ end
+
+ def init_compile_status(status, body, compile_root_p)
+ status.stack_size_for_pos = Fiddle.malloc(Fiddle::SIZEOF_INT * body.iseq_size)
+ body.iseq_size.times do |i|
+ status.stack_size_for_pos[i] = C.NOT_COMPILED_STACK_SIZE
+ end
+ if compile_root_p
+ status.inlined_iseqs = Fiddle.malloc(Fiddle::SIZEOF_VOIDP * body.iseq_size)
+ body.iseq_size.times do |i|
+ status.inlined_iseqs[i] = nil
+ end
+ end
+ if body.ci_size > 0
+ status.cc_entries_index = C.mjit_capture_cc_entries(status.compiled_iseq, body)
+ else
+ status.cc_entries_index = -1
+ end
+ if compile_root_p
+ status.compile_info = rb_mjit_iseq_compile_info(body)
+ else
+ status.compile_info = Fiddle.malloc(C.rb_mjit_compile_info.sizeof)
+ status.compile_info.disable_ivar_cache = false
+ status.compile_info.disable_exivar_cache = false
+ status.compile_info.disable_send_cache = false
+ status.compile_info.disable_inlining = false
+ status.compile_info.disable_const_cache = false
+ end
+ end
+
+ def using_ivar?(body)
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ case insn.name
+ when :getinstancevariable, :setinstancevariable
+ return true
+ end
+ pos += insn.len
+ end
+ return false
+ end
+
+ # Expand simple macro that doesn't require dynamic C code.
+ def expand_simple_macros(arg_expr)
+ arg_expr.dup.tap do |expr|
+ # For `leave`. We can't proceed next ISeq in the same JIT function.
+ expr.gsub!(/^(?<indent>\s*)RESTORE_REGS\(\);\n/) do
+ indent = Regexp.last_match[:indent]
+ <<-end.gsub(/^ {12}/, '')
+ #if OPT_CALL_THREADED_CODE
+ #{indent}rb_ec_thread_ptr(ec)->retval = val;
+ #{indent}return 0;
+ #else
+ #{indent}return val;
+ #endif
+ end
+ end
+ expr.gsub!(/^(?<indent>\s*)NEXT_INSN\(\);\n/) do
+ indent = Regexp.last_match[:indent]
+ <<-end.gsub(/^ {12}/, '')
+ #{indent}UNREACHABLE_RETURN(Qundef);
+ end
+ end
+ end
+ end
+
+ def to_cstr(expr)
+ expr.gsub(/^(?!#)/, ' ') # indent everything but preprocessor lines
+ end
+
+ # Interpret unsigned long as signed long (VALUE -> OFFSET)
+ def cast_offset(offset)
+ if offset >= 1 << 8 * Fiddle::SIZEOF_VOIDP - 1 # negative
+ offset -= 1 << 8 * Fiddle::SIZEOF_VOIDP
+ end
+ offset
+ end
+
+ def captured_cc_entries(status)
+ status.compiled_iseq.mjit_unit.cc_entries + status.cc_entries_index
+ end
+
+ def call_data_index(cd, body)
+ cd - body.call_data
+ end
+
+ def vm_cc_cme(cc)
+ # TODO: add VM_ASSERT like actual vm_cc_cme
+ cc.cme_
+ end
+
+ def def_iseq_ptr(method_def)
+ C.rb_iseq_check(method_def.body.iseq.iseqptr)
+ end
+
+ def rb_mjit_iseq_compile_info(body)
+ body.mjit_unit.compile_info
+ end
+
+ def ISEQ_IS_SIZE(body)
+ body.ic_size + body.ivc_size + body.ise_size + body.icvarc_size
+ end
+
+ # Return true if an object of the class may be a special const (immediate).
+ # It's "maybe" because Integer and Float are not guaranteed to be an immediate.
+ # If this returns false, rb_class_of could be optimzied to RBASIC_CLASS.
+ def maybe_special_const?(klass)
+ [
+ C.rb_cFalseClass,
+ C.rb_cNilClass,
+ C.rb_cTrueClass,
+ C.rb_cInteger,
+ C.rb_cSymbol,
+ C.rb_cFloat,
+ ].include?(klass)
+ end
+
+ def has_valid_method_type?(cc)
+ vm_cc_cme(cc) != nil
+ end
+
+ def already_compiled?(status, pos)
+ status.stack_size_for_pos[pos] != C.NOT_COMPILED_STACK_SIZE
+ end
+
+ # Return an iseq pointer if cc has inlinable iseq.
+ def rb_mjit_inlinable_iseq(ci, cc)
+ if has_valid_method_type?(cc) &&
+ C.vm_ci_flag(ci) & C.VM_CALL_TAILCALL == 0 && # inlining only non-tailcall path
+ vm_cc_cme(cc).def.type == C.VM_METHOD_TYPE_ISEQ &&
+ C.fastpath_applied_iseq_p(ci, cc, iseq = def_iseq_ptr(vm_cc_cme(cc).def)) &&
+ inlinable_iseq_p(iseq.body) # CC_SET_FASTPATH in vm_callee_setup_arg
+ return iseq
+ end
+ return nil
+ end
+
+ # Return true if the ISeq can be inlined without pushing a new control frame.
+ def inlinable_iseq_p(body)
+ # 1) If catch_except_p, caller frame should be preserved when callee catches an exception.
+ # Then we need to wrap `vm_exec()` but then we can't inline the call inside it.
+ #
+ # 2) If `body->catch_except_p` is false and `handles_sp?` of an insn is false,
+ # sp is not moved as we assume `status->local_stack_p = !body->catch_except_p`.
+ #
+ # 3) If `body->catch_except_p` is false and `always_leaf?` of an insn is true,
+ # pc is not moved.
+ if body.catch_except_p
+ return false
+ end
+
+ pos = 0
+ while pos < body.iseq_size
+ insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
+ # All insns in the ISeq except `leave` (to be overridden in the inlined code)
+ # should meet following strong assumptions:
+ # * Do not require `cfp->sp` motion
+ # * Do not move `cfp->pc`
+ # * Do not read any `cfp->pc`
+ if insn.name == :invokebuiltin || insn.name == :opt_invokebuiltin_delegate || insn.name == :opt_invokebuiltin_delegate_leave
+ # builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq
+ if !body.builtin_inline_p
+ return false;
+ end
+ elsif insn.name != :leave && C.insn_may_depend_on_sp_or_pc(insn.bin, body.iseq_encoded + (pos + 1))
+ return false
+ end
+ # At this moment, `cfp->ep` in an inlined method is not working.
+ case insn.name
+ when :getlocal,
+ :getlocal_WC_0,
+ :getlocal_WC_1,
+ :setlocal,
+ :setlocal_WC_0,
+ :setlocal_WC_1,
+ :getblockparam,
+ :getblockparamproxy,
+ :setblockparam
+ return false
+ end
+ pos += insn.len
+ end
+ return true
+ end
+
+ # CPointer::Struct could be nil on field reference, and this is a helper to
+ # handle that case while using CPointer::Struct#to_s in most cases.
+ # @param struct [RubyVM::MJIT::CPointer::Struct]
+ def to_addr(struct)
+ struct&.to_s || 'NULL'
+ end
+
+ def parent_shape_id(shape_id)
+ return shape_id if shape_id == C.INVALID_SHAPE_ID
+
+ parent_id = C.rb_shape_get_shape_by_id(shape_id).parent_id
+ parent = C.rb_shape_get_shape_by_id(parent_id)
+
+ if parent.type == C.SHAPE_CAPACITY_CHANGE
+ parent.parent_id
+ else
+ parent_id
+ end
+ end
+end
diff --git a/lib/ruby_vm/mjit/hooks.rb b/lib/ruby_vm/mjit/hooks.rb
new file mode 100644
index 0000000000..3fb1004111
--- /dev/null
+++ b/lib/ruby_vm/mjit/hooks.rb
@@ -0,0 +1,32 @@
+module RubyVM::MJIT::Hooks # :nodoc: all
+ C = RubyVM::MJIT.const_get(:C, false)
+
+ def self.on_bop_redefined(_redefined_flag, _bop)
+ C.mjit_cancel_all("BOP is redefined")
+ end
+
+ def self.on_cme_invalidate(_cme)
+ # to be used later
+ end
+
+ def self.on_ractor_spawn
+ C.mjit_cancel_all("Ractor is spawned")
+ end
+
+ def self.on_constant_state_changed(_id)
+ # to be used later
+ end
+
+ def self.on_constant_ic_update(_iseq, _ic, _insn_idx)
+ # to be used later
+ end
+
+ def self.on_tracing_invalidate_all(new_iseq_events)
+ # Stop calling all JIT-ed code. We can't rewrite existing JIT-ed code to trace_ insns for now.
+ # :class events are triggered only in ISEQ_TYPE_CLASS, but mjit_target_iseq_p ignores such iseqs.
+ # Thus we don't need to cancel JIT-ed code for :class events.
+ if new_iseq_events != C.RUBY_EVENT_CLASS
+ C.mjit_cancel_all("TracePoint is enabled")
+ end
+ end
+end
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 915a899f38..af86646a82 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -8,7 +9,7 @@
require "rbconfig"
module Gem
- VERSION = "3.4.0.dev".freeze
+ VERSION = "3.4.19"
end
# Must be first since it unloads the prelude from 1.9.2
@@ -119,10 +120,6 @@ module Gem
# to avoid deprecation warnings in Ruby 2.7.
UNTAINT = RUBY_VERSION < "2.7" ? :untaint.to_sym : proc {}
- # When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
- KERNEL_WARN_IGNORES_INTERNAL_ENTRIES = RUBY_ENGINE == "truffleruby" ||
- (RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.0")
-
##
# An Array of Regexps that match windows Ruby platforms.
@@ -185,6 +182,8 @@ module Gem
@default_source_date_epoch = nil
+ @discover_gems_on_require = true
+
##
# Try to activate a gem containing +path+. Returns true if
# activation succeeded or wasn't needed because it was already
@@ -774,6 +773,10 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
open_file(path, "wb") do |io|
io.write data
end
+ rescue Errno::ENOSPC
+ # If we ran out of space but the file exists, it's *guaranteed* to be corrupted.
+ File.delete(path) if File.exist?(path)
+ raise
end
##
@@ -822,7 +825,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def self.env_requirement(gem_name)
@env_requirements_by_name ||= {}
@env_requirements_by_name[gem_name] ||= begin
- req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0".freeze
+ req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0"
Gem::Requirement.create(req)
end
end
@@ -853,8 +856,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Returns the version of the latest release-version of gem +name+
def self.latest_version_for(name)
- spec = latest_spec_for name
- spec && spec.version
+ latest_spec_for(name)&.version
end
##
@@ -1163,9 +1165,17 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# RubyGems distributors (like operating system package managers) can
# disable RubyGems update by setting this to error message printed to
# end-users on gem update --system instead of actual update.
+
attr_accessor :disable_system_update_message
##
+ # Whether RubyGems should enhance builtin `require` to automatically
+ # check whether the path required is present in installed gems, and
+ # automatically activate them and add them to `$LOAD_PATH`.
+
+ attr_accessor :discover_gems_on_require
+
+ ##
# Hash of loaded Gem::Specification keyed by name
attr_reader :loaded_specs
@@ -1291,9 +1301,8 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Location of Marshal quick gemspecs on remote repositories
- MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
- autoload :BundlerVersionFinder, File.expand_path("rubygems/bundler_version_finder", __dir__)
autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__)
autoload :Dependency, File.expand_path("rubygems/dependency", __dir__)
autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__)
@@ -1346,5 +1355,16 @@ end
Gem::Specification.load_defaults
require_relative "rubygems/core_ext/kernel_gem"
-require_relative "rubygems/core_ext/kernel_require"
-require_relative "rubygems/core_ext/kernel_warn"
+
+path = File.join(__dir__, "rubygems/core_ext/kernel_require.rb")
+# When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
+if RUBY_ENGINE == "truffleruby" ||
+ (RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.0")
+ file = "<internal:#{path}>"
+else
+ require_relative "rubygems/core_ext/kernel_warn"
+ file = path
+end
+eval File.read(path), nil, file
+
+require ENV["BUNDLER_SETUP"] if ENV["BUNDLER_SETUP"] && !defined?(Bundler)
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index 58b601f6b0..2ab3e137da 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::AvailableSet
include Enumerable
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index dcc64e6409..5d198114e0 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# BasicSpecification is an abstract class which implements some common code
# used by both Specification and StubSpecification.
diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb
index f6fad0bd83..5b34227d3a 100644
--- a/lib/rubygems/bundler_version_finder.rb
+++ b/lib/rubygems/bundler_version_finder.rb
@@ -21,7 +21,7 @@ module Gem::BundlerVersionFinder
end
def self.bundle_update_bundler_version
- return unless File.basename($0) == "bundle".freeze
+ return unless File.basename($0) == "bundle"
return unless "update".start_with?(ARGV.first || " ")
bundler_version = nil
update_index = nil
@@ -47,7 +47,7 @@ module Gem::BundlerVersionFinder
def self.lockfile_contents
gemfile = ENV["BUNDLE_GEMFILE"]
- gemfile = nil if gemfile && gemfile.empty?
+ gemfile = nil if gemfile&.empty?
unless gemfile
begin
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index badc21023a..07cf4e0e81 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -201,11 +202,15 @@ class Gem::Command
# respectively.
def get_all_gem_names_and_versions
get_all_gem_names.map do |name|
- if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
- [$1, $2]
- else
- [name]
- end
+ extract_gem_name_and_version(name)
+ end
+ end
+
+ def extract_gem_name_and_version(name) # :nodoc:
+ if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
+ [$1, $2]
+ else
+ [name]
end
end
@@ -624,13 +629,17 @@ class Gem::Command
# :stopdoc:
- HELP = <<-HELP.freeze
+ HELP = <<-HELP
RubyGems is a package manager for Ruby.
Usage:
gem -h/--help
gem -v/--version
- gem command [arguments...] [options...]
+ gem [global options...] command [arguments...] [options...]
+
+ Global options:
+ -C PATH run as if gem was started in <PATH>
+ instead of the current working directory
Examples:
gem install rake
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 9d49138ae7..b3913d465a 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -43,6 +44,7 @@ class Gem::CommandManager
:contents,
:dependency,
:environment,
+ :exec,
:fetch,
:generate_index,
:help,
@@ -73,8 +75,8 @@ class Gem::CommandManager
].freeze
ALIAS_COMMANDS = {
- "i" => "install",
- "login" => "signin",
+ "i" => "install",
+ "login" => "signin",
"logout" => "signout",
}.freeze
@@ -82,7 +84,7 @@ class Gem::CommandManager
# Return the authoritative instance of the command manager.
def self.instance
- @command_manager ||= new
+ @instance ||= new
end
##
@@ -97,7 +99,7 @@ class Gem::CommandManager
# Reset the authoritative instance of the command manager.
def self.reset
- @command_manager = nil
+ @instance = nil
end
##
@@ -175,14 +177,20 @@ class Gem::CommandManager
when "-v", "--version" then
say Gem::VERSION
terminate_interaction 0
+ when "-C" then
+ args.shift
+ start_point = args.shift
+ if Dir.exist?(start_point)
+ Dir.chdir(start_point) { invoke_command(args, build_args) }
+ else
+ alert_error clean_text("#{start_point} isn't a directory.")
+ terminate_interaction 1
+ end
when /^-/ then
alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.")
terminate_interaction 1
else
- cmd_name = args.shift.downcase
- cmd = find_command cmd_name
- cmd.deprecation_warning if cmd.deprecated?
- cmd.invoke_with_build_args args, build_args
+ invoke_command(args, build_args)
end
end
@@ -237,4 +245,11 @@ class Gem::CommandManager
ui.backtrace e
end
end
+
+ def invoke_command(args, build_args)
+ cmd_name = args.shift.downcase
+ cmd = find_command cmd_name
+ cmd.deprecation_warning if cmd.deprecated?
+ cmd.invoke_with_build_args args, build_args
+ end
end
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index accbd7e97d..68e020d94a 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../package"
require_relative "../version_option"
@@ -26,6 +27,9 @@ class Gem::Commands::BuildCommand < Gem::Command
add_option "-C PATH", "Run as if gem build was started in <PATH> instead of the current working directory." do |value, options|
options[:build_path] = value
end
+ deprecate_option "-C",
+ version: "4.0",
+ extra_msg: "-C is a global flag now. Use `gem -C PATH build GEMSPEC_FILE [options]` instead"
end
def arguments # :nodoc:
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 17b1d11b19..344f31fba9 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../security"
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 4d1f8782b1..b300b4f87a 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
require_relative "../validator"
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 1ae84924c1..ee5e1252b6 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../dependency_list"
require_relative "../uninstaller"
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index c5fdfca31e..f378441ca4 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index 3f69a95e83..cef98e30d3 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../version_option"
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index d95e1d0dbb..28dbf93c50 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::EnvironmentCommand < Gem::Command
diff --git a/lib/rubygems/commands/exec_command.rb b/lib/rubygems/commands/exec_command.rb
new file mode 100644
index 0000000000..383c8c5b37
--- /dev/null
+++ b/lib/rubygems/commands/exec_command.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+require_relative "../command"
+require_relative "../dependency_installer"
+require_relative "../gem_runner"
+require_relative "../package"
+require_relative "../version_option"
+
+class Gem::Commands::ExecCommand < Gem::Command
+ include Gem::VersionOption
+
+ def initialize
+ super "exec", "Run a command from a gem", {
+ version: Gem::Requirement.default,
+ }
+
+ add_version_option
+ add_prerelease_option "to be installed"
+
+ add_option "-g", "--gem GEM", "run the executable from the given gem" do |value, options|
+ options[:gem_name] = value
+ end
+
+ add_option(:"Install/Update", "--conservative",
+ "Prefer the most recent installed version, ",
+ "rather than the latest version overall") do |value, options|
+ options[:conservative] = true
+ end
+ end
+
+ def arguments # :nodoc:
+ "COMMAND the executable command to run"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The exec command handles installing (if necessary) and running an executable
+from a gem, regardless of whether that gem is currently installed.
+
+The exec command can be thought of as a shortcut to running `gem install` and
+then the executable from the installed gem.
+
+For example, `gem exec rails new .` will run `rails new .` in the current
+directory, without having to manually run `gem install rails`.
+Additionally, the exec command ensures the most recent version of the gem
+is used (unless run with `--conservative`), and that the gem is not installed
+to the same gem path as user-installed gems.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [options --] COMMAND [args]"
+ end
+
+ def execute
+ gem_paths = { "GEM_HOME" => Gem.paths.home, "GEM_PATH" => Gem.paths.path.join(File::PATH_SEPARATOR), "GEM_SPEC_CACHE" => Gem.paths.spec_cache_dir }.compact
+
+ check_executable
+
+ print_command
+ if options[:gem_name] == "gem" && options[:executable] == "gem"
+ set_gem_exec_install_paths
+ Gem::GemRunner.new.run options[:args]
+ return
+ elsif options[:conservative]
+ install_if_needed
+ else
+ install
+ activate!
+ end
+
+ load!
+ ensure
+ ENV.update(gem_paths) if gem_paths
+ Gem.clear_paths
+ end
+
+ private
+
+ def handle_options(args)
+ args = add_extra_args(args)
+ check_deprecated_options(args)
+ @options = Marshal.load Marshal.dump @defaults # deep copy
+ parser.order!(args) do |v|
+ # put the non-option back at the front of the list of arguments
+ args.unshift(v)
+
+ # stop parsing once we hit the first non-option,
+ # so you can call `gem exec rails --version` and it prints the rails
+ # version rather than rubygem's
+ break
+ end
+ @options[:args] = args
+
+ options[:executable], gem_version = extract_gem_name_and_version(options[:args].shift)
+ options[:gem_name] ||= options[:executable]
+
+ if gem_version
+ if options[:version].none?
+ options[:version] = Gem::Requirement.new(gem_version)
+ else
+ options[:version].concat [gem_version]
+ end
+ end
+
+ if options[:prerelease] && !options[:version].prerelease?
+ if options[:version].none?
+ options[:version] = Gem::Requirement.default_prerelease
+ else
+ options[:version].concat [Gem::Requirement.default_prerelease]
+ end
+ end
+ end
+
+ def check_executable
+ if options[:executable].nil?
+ raise Gem::CommandLineError,
+ "Please specify an executable to run (e.g. #{program_name} COMMAND)"
+ end
+ end
+
+ def print_command
+ verbose "running #{program_name} with:\n"
+ opts = options.reject {|_, v| v.nil? || Array(v).empty? }
+ max_length = opts.map {|k, _| k.size }.max
+ opts.each do |k, v|
+ next if v.nil?
+ verbose "\t#{k.to_s.rjust(max_length)}: #{v}"
+ end
+ verbose ""
+ end
+
+ def install_if_needed
+ activate!
+ rescue Gem::MissingSpecError
+ verbose "#{Gem::Dependency.new(options[:gem_name], options[:version])} not available locally, installing from remote"
+ install
+ activate!
+ end
+
+ def set_gem_exec_install_paths
+ home = File.join(Gem.dir, "gem_exec")
+
+ ENV["GEM_PATH"] = ([home] + Gem.path).join(File::PATH_SEPARATOR)
+ ENV["GEM_HOME"] = home
+ Gem.clear_paths
+ end
+
+ def install
+ set_gem_exec_install_paths
+
+ gem_name = options[:gem_name]
+ gem_version = options[:version]
+
+ install_options = options.merge(
+ minimal_deps: false,
+ wrappers: true
+ )
+
+ suppress_always_install do
+ dep_installer = Gem::DependencyInstaller.new install_options
+
+ request_set = dep_installer.resolve_dependencies gem_name, gem_version
+
+ verbose "Gems to install:"
+ request_set.sorted_requests.each do |activation_request|
+ verbose "\t#{activation_request.full_name}"
+ end
+
+ request_set.install install_options
+ end
+
+ Gem::Specification.reset
+ rescue Gem::InstallError => e
+ alert_error "Error installing #{gem_name}:\n\t#{e.message}"
+ terminate_interaction 1
+ rescue Gem::GemNotFoundException => e
+ show_lookup_failure e.name, e.version, e.errors, false
+
+ terminate_interaction 2
+ rescue Gem::UnsatisfiableDependencyError => e
+ show_lookup_failure e.name, e.version, e.errors, false,
+ "'#{gem_name}' (#{gem_version})"
+
+ terminate_interaction 2
+ end
+
+ def activate!
+ gem(options[:gem_name], options[:version])
+ Gem.finish_resolve
+
+ verbose "activated #{options[:gem_name]} (#{Gem.loaded_specs[options[:gem_name]].version})"
+ end
+
+ def load!
+ argv = ARGV.clone
+ ARGV.replace options[:args]
+
+ exe = executable = options[:executable]
+
+ contains_executable = Gem.loaded_specs.values.select do |spec|
+ spec.executables.include?(executable)
+ end
+
+ if contains_executable.any? {|s| s.name == executable }
+ contains_executable.select! {|s| s.name == executable }
+ end
+
+ if contains_executable.empty?
+ if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable)
+ contains_executable << spec
+ else
+ alert_error "Failed to load executable `#{executable}`," \
+ " are you sure the gem `#{options[:gem_name]}` contains it?"
+ terminate_interaction 1
+ end
+ end
+
+ if contains_executable.size > 1
+ alert_error "Ambiguous which gem `#{executable}` should come from: " \
+ "the options are #{contains_executable.map(&:name)}, " \
+ "specify one via `-g`"
+ terminate_interaction 1
+ end
+
+ load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a")
+ ensure
+ ARGV.replace argv
+ end
+
+ def suppress_always_install
+ name = :always_install
+ cls = ::Gem::Resolver::InstallerSet
+ method = cls.instance_method(name)
+ cls.remove_method(name)
+ cls.define_method(name) { [] }
+
+ begin
+ yield
+ ensure
+ cls.remove_method(name)
+ cls.define_method(name, method)
+ end
+ end
+end
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 3a8c435d0e..950d4fe30e 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../version_option"
@@ -10,7 +11,7 @@ class Gem::Commands::FetchCommand < Gem::Command
def initialize
defaults = {
:suggest_alternate => true,
- :version => Gem::Requirement.default,
+ :version => Gem::Requirement.default,
}
super "fetch", "Download a gem and place it in the current directory", defaults
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index bc71e60ff0..ce580cfaf9 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../indexer"
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 8bfb4458ff..20b99fa366 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::HelpCommand < Gem::Command
# :stopdoc:
- EXAMPLES = <<-EOF.freeze
+ EXAMPLES = <<-EOF
Some examples of 'gem' usage.
* Install 'rake', either from local directory or remote server:
@@ -52,7 +53,7 @@ Some examples of 'gem' usage.
gem update --system
EOF
- GEM_DEPENDENCIES = <<-EOF.freeze
+ GEM_DEPENDENCIES = <<-EOF
A gem dependencies file allows installation of a consistent set of gems across
multiple environments. The RubyGems implementation is designed to be
compatible with Bundler's Gemfile format. You can see additional
@@ -229,7 +230,7 @@ default. This may be overridden with the :development_group option:
EOF
- PLATFORMS = <<-'EOF'.freeze
+ PLATFORMS = <<-'EOF'
RubyGems platforms are composed of three parts, a CPU, an OS, and a
version. These values are taken from values in rbconfig.rb. You can view
your current platform by running `gem environment`.
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 724b4fe51d..b0bc2908b4 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../install_update_options"
require_relative "../dependency_installer"
require_relative "../local_remote_options"
require_relative "../validator"
require_relative "../version_option"
+require_relative "../update_suggestion"
##
# Gem installer command line tool
@@ -17,14 +19,15 @@ class Gem::Commands::InstallCommand < Gem::Command
include Gem::VersionOption
include Gem::LocalRemoteOptions
include Gem::InstallUpdateOptions
+ include Gem::UpdateSuggestion
def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:format_executable => false,
- :lock => true,
+ :lock => true,
:suggest_alternate => true,
- :version => Gem::Requirement.default,
- :without_groups => [],
+ :version => Gem::Requirement.default,
+ :without_groups => [],
})
defaults.merge!(install_update_options)
@@ -168,6 +171,8 @@ You can use `i` command instead of `install`.
show_installed
+ say update_suggestion if eglible_for_update?
+
terminate_interaction exit_code
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 011873b99c..522c771f90 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../query_utils"
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index da636492c9..3a9512fe3f 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::LockCommand < Gem::Command
diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb
index b633cd3d81..b91a8db12d 100644
--- a/lib/rubygems/commands/mirror_command.rb
+++ b/lib/rubygems/commands/mirror_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
unless defined? Gem::Commands::MirrorCommand
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index d5283f72dd..5a13074a1d 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
index 1785194389..08a9221a26 100644
--- a/lib/rubygems/commands/outdated_command.rb
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../spec_fetcher"
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 4a0f7aa3e4..b51c9cf888 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../gemcutter_utilities"
@@ -15,7 +16,7 @@ The owner command lets you add and remove owners of a gem on a push
server (the default is https://rubygems.org). Multiple owners can be
added or removed at the same time, if the flag is given multiple times.
-The supported user identifiers are dependant on the push server.
+The supported user identifiers are dependent on the push server.
For rubygems.org, both e-mail and handle are supported, even though the
user identifier field is called "email".
@@ -98,8 +99,10 @@ permission to.
action = method == :delete ? "Removing" : "Adding"
with_response response, "#{action} #{owner}"
- rescue
- # ignore
+ rescue Gem::WebauthnVerificationError => e
+ raise e
+ rescue StandardError
+ # ignore early exits to allow for completing the iteration of all owners
end
end
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 72db53ef37..64608a033f 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../package"
require_relative "../installer"
@@ -34,6 +35,11 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:extensions] = value
end
+ add_option("--only-missing-extensions",
+ "Only restore gems with missing extensions") do |value, options|
+ options[:only_missing_extensions] = value
+ end
+
add_option("--only-executables",
"Only restore executables") do |value, options|
options[:only_executables] = value
@@ -107,6 +113,10 @@ extensions will be restored.
Gem::Specification.select do |spec|
spec.extensions && !spec.extensions.empty?
end
+ elsif options[:only_missing_extensions]
+ Gem::Specification.select do |spec|
+ spec.missing_extensions?
+ end
else
get_all_gem_names.sort.map do |gem_name|
Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 46b65f4e15..79ca3d59b0 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../gemcutter_utilities"
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index c6315acf8c..4fa201272e 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../query_utils"
require_relative "../deprecate"
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index a998a9704c..e318a52914 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
require_relative "../rdoc"
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index 3f8f7e13f2..c7469e1fa8 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../query_utils"
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index 56be07c79d..f1dde4aa02 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
unless defined? Gem::Commands::ServerCommand
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index c782c3618c..df1732b49e 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
##
@@ -107,7 +108,7 @@ class Gem::Commands::SetupCommand < Gem::Command
end
def check_ruby_version
- required_version = Gem::Requirement.new ">= 2.3.0"
+ required_version = Gem::Requirement.new ">= 2.6.0"
unless required_version.satisfied_by? Gem.ruby_version
alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}"
@@ -244,7 +245,7 @@ By default, this RubyGems will install gem as:
def install_executables(bin_dir)
prog_mode = options[:prog_mode] || 0755
- executables = { "gem" => "bin" }
+ executables = { "gem" => "exe" }
executables.each do |tool, path|
say "Installing #{tool} executable" if @verbose
@@ -576,7 +577,7 @@ abort "#{deprecation_message}"
require_relative "../uninstaller"
ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true,
- :version => "< 0.4")
+ :version => "< 0.4")
ui.uninstall
rescue Gem::InstallError
end
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
index 2660eee4f3..0f77908c5b 100644
--- a/lib/rubygems/commands/signin_command.rb
+++ b/lib/rubygems/commands/signin_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../gemcutter_utilities"
diff --git a/lib/rubygems/commands/signout_command.rb b/lib/rubygems/commands/signout_command.rb
index fa688ea3f8..bdd01e4393 100644
--- a/lib/rubygems/commands/signout_command.rb
+++ b/lib/rubygems/commands/signout_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::SignoutCommand < Gem::Command
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index 5a8f5af9c3..85cc68ac09 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../remote_fetcher"
require_relative "../spec_fetcher"
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 12004a6d56..ed158506e0 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../version_option"
diff --git a/lib/rubygems/commands/stale_command.rb b/lib/rubygems/commands/stale_command.rb
index 0246f42e3e..a94d77c193 100644
--- a/lib/rubygems/commands/stale_command.rb
+++ b/lib/rubygems/commands/stale_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::StaleCommand < Gem::Command
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 3c520826e5..feba1c5b7c 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
require_relative "../uninstaller"
@@ -125,6 +126,9 @@ that is a dependency of an existing gem. You can use the
def execute
check_version
+ # Consider only gem specifications installed at `--install-dir`
+ Gem::Specification.dirs = options[:install_dir] if options[:install_dir]
+
if options[:all] && !options[:args].empty?
uninstall_specific
elsif options[:all]
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index a365e85416..1bff53429b 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../version_option"
require_relative "../security_option"
@@ -21,7 +22,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
super "unpack", "Unpack an installed gem to the current directory",
:version => Gem::Requirement.default,
- :target => Dir.pwd
+ :target => Dir.pwd
add_option("--target=DIR",
"target directory for unpacking") do |value, options|
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 7c24fedcde..6f084d1c38 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../command_manager"
require_relative "../dependency_installer"
@@ -329,14 +330,8 @@ command to remove old versions.
Gem::Version.new("3.2.3")
elsif Gem.ruby_version > Gem::Version.new("2.7.a")
Gem::Version.new("3.1.2")
- elsif Gem.ruby_version > Gem::Version.new("2.6.a")
- Gem::Version.new("3.0.1")
- elsif Gem.ruby_version > Gem::Version.new("2.5.a")
- Gem::Version.new("2.7.3")
- elsif Gem.ruby_version > Gem::Version.new("2.4.a")
- Gem::Version.new("2.6.8")
else
- Gem::Version.new("2.5.2")
+ Gem::Version.new("3.0.1")
end
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index 5b9a79b734..ec464d9672 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
class Gem::Commands::WhichCommand < Gem::Command
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 1499f72f5d..d344a020c3 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../version_option"
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index c53e209ae8..68c653269e 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -371,11 +372,44 @@ if you believe they were disclosed to a third party.
@backtrace || $DEBUG
end
+ # Check state file is writable. Creates empty file if not present to ensure we can write to it.
+ def state_file_writable?
+ if File.exist?(state_file_name)
+ File.writable?(state_file_name)
+ else
+ require "fileutils"
+ FileUtils.mkdir_p File.dirname(state_file_name)
+ File.open(state_file_name, "w") {}
+ true
+ end
+ rescue Errno::EACCES
+ false
+ end
+
# The name of the configuration file.
def config_file_name
@config_file_name || Gem.config_file
end
+ # The name of the state file.
+ def state_file_name
+ Gem.state_file
+ end
+
+ # Reads time of last update check from state file
+ def last_update_check
+ if File.readable?(state_file_name)
+ File.read(state_file_name).to_i
+ else
+ 0
+ end
+ end
+
+ # Writes time of last update check to state file
+ def last_update_check=(timestamp)
+ File.write(state_file_name, timestamp.to_s) if state_file_writable?
+ end
+
# Delegates to @hash
def each(&block)
hash = @hash.dup
diff --git a/lib/rubygems/core_ext/kernel_gem.rb b/lib/rubygems/core_ext/kernel_gem.rb
index 6f96cab84f..b2f97b9ed9 100644
--- a/lib/rubygems/core_ext/kernel_gem.rb
+++ b/lib/rubygems/core_ext/kernel_gem.rb
@@ -1,9 +1,4 @@
# frozen_string_literal: true
-##
-# RubyGems adds the #gem method to allow activation of specific gem versions
-# and overrides the #require method on Kernel to make gems appear as if they
-# live on the <code>$LOAD_PATH</code>. See the documentation of these methods
-# for further detail.
module Kernel
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index 8064d813e1..0869c8746f 100644
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -13,12 +14,12 @@ module Kernel
# Make sure we have a reference to Ruby's original Kernel#require
unless defined?(gem_original_require)
+ # :stopdoc:
alias gem_original_require require
private :gem_original_require
+ # :startdoc:
end
- file = Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES ? "<internal:#{__FILE__}>" : __FILE__
- module_eval <<'RUBY', file, __LINE__ + 1 # rubocop:disable Style/EvalWithLocation
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
@@ -33,142 +34,135 @@ module Kernel
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
- def require(path)
- if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
- monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?
- end
- RUBYGEMS_ACTIVATION_MONITOR.enter
-
- path = path.to_path if path.respond_to? :to_path
-
- if spec = Gem.find_unresolved_default_spec(path)
- # Ensure -I beats a default gem
- resolved_path = begin
- rp = nil
- load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
- Gem.suffixes.each do |s|
- $LOAD_PATH[0...load_path_check_index].each do |lp|
- safe_lp = lp.dup.tap(&Gem::UNTAINT)
- begin
- if File.symlink? safe_lp # for backward compatibility
- next
+ def require(path) # :doc:
+ return gem_original_require(path) unless Gem.discover_gems_on_require
+
+ begin
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ path = path.to_path if path.respond_to? :to_path
+
+ if spec = Gem.find_unresolved_default_spec(path)
+ # Ensure -I beats a default gem
+ resolved_path = begin
+ rp = nil
+ load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
+ Gem.suffixes.each do |s|
+ $LOAD_PATH[0...load_path_check_index].each do |lp|
+ safe_lp = lp.dup.tap(&Gem::UNTAINT)
+ begin
+ if File.symlink? safe_lp # for backward compatibility
+ next
+ end
+ rescue SecurityError
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise
end
- rescue SecurityError
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
- end
- full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
- if File.file?(full_path)
- rp = full_path
- break
+ full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
+ if File.file?(full_path)
+ rp = full_path
+ break
+ end
end
+ break if rp
end
- break if rp
+ rp
end
- rp
- end
-
- begin
- Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
- rescue Exception
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
- end unless resolved_path
- end
-
- # If there are no unresolved deps, then we can use just try
- # normal require handle loading a gem from the rescue below.
-
- if Gem::Specification.unresolved_deps.empty?
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- end
-
- # If +path+ is for a gem that has already been loaded, don't
- # bother trying to find it in an unresolved gem, just go straight
- # to normal require.
- #--
- # TODO request access to the C implementation of this to speed up RubyGems
-
- if Gem::Specification.find_active_stub_by_path(path)
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- end
- # Attempt to find +path+ in any unresolved gems...
-
- found_specs = Gem::Specification.find_in_unresolved path
-
- # If there are no directly unresolved gems, then try and find +path+
- # in any gems that are available via the currently unresolved gems.
- # For example, given:
- #
- # a => b => c => d
- #
- # If a and b are currently active with c being unresolved and d.rb is
- # requested, then find_in_unresolved_tree will find d.rb in d because
- # it's a dependency of c.
- #
- if found_specs.empty?
- found_specs = Gem::Specification.find_in_unresolved_tree path
-
- found_specs.each do |found_spec|
- found_spec.activate
+ begin
+ Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
+ rescue Exception
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise
+ end unless resolved_path
end
- # We found +path+ directly in an unresolved gem. Now we figure out, of
- # the possible found specs, which one we should activate.
- else
-
- # Check that all the found specs are just different
- # versions of the same gem
- names = found_specs.map(&:name).uniq
+ # If there are no unresolved deps, then we can use just try
+ # normal require handle loading a gem from the rescue below.
- if names.size > 1
+ if Gem::Specification.unresolved_deps.empty?
RUBYGEMS_ACTIVATION_MONITOR.exit
- raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
+ return gem_original_require(path)
end
- # Ok, now find a gem that has no conflicts, starting
- # at the highest version.
- valid = found_specs.find {|s| !s.has_conflicts? }
+ # If +path+ is for a gem that has already been loaded, don't
+ # bother trying to find it in an unresolved gem, just go straight
+ # to normal require.
+ #--
+ # TODO request access to the C implementation of this to speed up RubyGems
- unless valid
- le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
- le.name = names.first
+ if Gem::Specification.find_active_stub_by_path(path)
RUBYGEMS_ACTIVATION_MONITOR.exit
- raise le
+ return gem_original_require(path)
end
- valid.activate
- end
+ # Attempt to find +path+ in any unresolved gems...
+
+ found_specs = Gem::Specification.find_in_unresolved path
+
+ # If there are no directly unresolved gems, then try and find +path+
+ # in any gems that are available via the currently unresolved gems.
+ # For example, given:
+ #
+ # a => b => c => d
+ #
+ # If a and b are currently active with c being unresolved and d.rb is
+ # requested, then find_in_unresolved_tree will find d.rb in d because
+ # it's a dependency of c.
+ #
+ if found_specs.empty?
+ found_specs = Gem::Specification.find_in_unresolved_tree path
+
+ found_specs.each do |found_spec|
+ found_spec.activate
+ end
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- rescue LoadError => load_error
- RUBYGEMS_ACTIVATION_MONITOR.enter
+ # We found +path+ directly in an unresolved gem. Now we figure out, of
+ # the possible found specs, which one we should activate.
+ else
- begin
- if load_error.path == path and Gem.try_activate(path)
- require_again = true
+ # Check that all the found specs are just different
+ # versions of the same gem
+ names = found_specs.map(&:name).uniq
+
+ if names.size > 1
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
+ end
+
+ # Ok, now find a gem that has no conflicts, starting
+ # at the highest version.
+ valid = found_specs.find {|s| !s.has_conflicts? }
+
+ unless valid
+ le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
+ le.name = names.first
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise le
+ end
+
+ valid.activate
end
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.exit
- end
- return gem_original_require(path) if require_again
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
+ rescue LoadError => load_error
+ if load_error.path == path
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ begin
+ require_again = Gem.try_activate(path)
+ ensure
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ end
- raise load_error
- ensure
- if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
- if monitor_owned != (ow = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?)
- STDERR.puts [$$, Thread.current, $!, $!.backtrace].inspect if $!
- raise "CRITICAL: RUBYGEMS_ACTIVATION_MONITOR.owned?: before #{monitor_owned} -> after #{ow}"
+ return gem_original_require(path) if require_again
end
+
+ raise load_error
end
end
-RUBY
private :require
diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb
index 8f43e00456..1f4c77f04b 100644
--- a/lib/rubygems/core_ext/kernel_warn.rb
+++ b/lib/rubygems/core_ext/kernel_warn.rb
@@ -1,54 +1,50 @@
# frozen_string_literal: true
-# `uplevel` keyword argument of Kernel#warn is available since ruby 2.5.
-if RUBY_VERSION >= "2.5" && !Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES
+module Kernel
+ rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path.
- module Kernel
- rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path.
+ original_warn = instance_method(:warn)
- original_warn = instance_method(:warn)
+ remove_method :warn
+ class << self
remove_method :warn
+ end
- class << self
- remove_method :warn
+ module_function define_method(:warn) {|*messages, **kw|
+ unless uplevel = kw[:uplevel]
+ if Gem.java_platform? && RUBY_VERSION < "3.1"
+ return original_warn.bind(self).call(*messages)
+ else
+ return original_warn.bind(self).call(*messages, **kw)
+ end
end
- module_function define_method(:warn) {|*messages, **kw|
- unless uplevel = kw[:uplevel]
- if Gem.java_platform?
- return original_warn.bind(self).call(*messages)
- else
- return original_warn.bind(self).call(*messages, **kw)
+ # Ensure `uplevel` fits a `long`
+ uplevel, = [uplevel].pack("l!").unpack("l!")
+
+ if uplevel >= 0
+ start = 0
+ while uplevel >= 0
+ loc, = caller_locations(start, 1)
+ unless loc
+ # No more backtrace
+ start += uplevel
+ break
end
- end
- # Ensure `uplevel` fits a `long`
- uplevel, = [uplevel].pack("l!").unpack("l!")
-
- if uplevel >= 0
- start = 0
- while uplevel >= 0
- loc, = caller_locations(start, 1)
- unless loc
- # No more backtrace
- start += uplevel
- break
- end
+ start += 1
- start += 1
-
- if path = loc.path
- unless path.start_with?(rubygems_path) || path.start_with?("<internal:")
- # Non-rubygems frames
- uplevel -= 1
- end
+ if path = loc.path
+ unless path.start_with?(rubygems_path) || path.start_with?("<internal:")
+ # Non-rubygems frames
+ uplevel -= 1
end
end
- kw[:uplevel] = start
end
+ kw[:uplevel] = start
+ end
- original_warn.bind(self).call(*messages, **kw)
- }
- end
+ original_warn.bind(self).call(*messages, **kw)
+ }
end
diff --git a/lib/rubygems/core_ext/tcpsocket_init.rb b/lib/rubygems/core_ext/tcpsocket_init.rb
index c9e0a92953..451ffa50b2 100644
--- a/lib/rubygems/core_ext/tcpsocket_init.rb
+++ b/lib/rubygems/core_ext/tcpsocket_init.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "socket"
module CoreExtensions
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index e12c13cb46..2b1baa333d 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
+
module Gem
- DEFAULT_HOST = "https://rubygems.org".freeze
+ DEFAULT_HOST = "https://rubygems.org"
@post_install_hooks ||= []
@done_installing_hooks ||= []
@@ -134,6 +135,13 @@ module Gem
end
##
+ # The path to standard location of the user's state file.
+
+ def self.state_file
+ @state_file ||= File.join(Gem.state_home, "gem", "last_update_check").tap(&Gem::UNTAINT)
+ end
+
+ ##
# The path to standard location of the user's cache directory.
def self.cache_home
@@ -148,6 +156,13 @@ module Gem
end
##
+ # The path to standard location of the user's state directory.
+
+ def self.state_home
+ @state_home ||= (ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state"))
+ end
+
+ ##
# How String Gem paths should be split. Overridable for esoteric platforms.
def self.path_separator
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 85f8609677..d9e0c07b0f 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The Dependency class holds a Gem name and a Gem::Requirement.
@@ -277,7 +278,10 @@ class Gem::Dependency
requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
end.map(&:to_spec)
- Gem::BundlerVersionFinder.prioritize!(matches) if prioritizes_bundler?
+ if prioritizes_bundler?
+ require_relative "bundler_version_finder"
+ Gem::BundlerVersionFinder.prioritize!(matches)
+ end
if platform_only
matches.reject! do |spec|
@@ -296,7 +300,7 @@ class Gem::Dependency
end
def prioritizes_bundler?
- name == "bundler".freeze && !specific?
+ name == "bundler" && !specific?
end
def to_specs
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index aa7b66d36b..a8d73e521b 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "dependency_list"
require_relative "package"
@@ -16,16 +17,16 @@ class Gem::DependencyInstaller
extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
- :env_shebang => false,
- :document => %w[ri],
- :domain => :both, # HACK dup
- :force => false,
- :format_executable => false, # HACK dup
+ :env_shebang => false,
+ :document => %w[ri],
+ :domain => :both, # HACK dup
+ :force => false,
+ :format_executable => false, # HACK dup
:ignore_dependencies => false,
- :prerelease => false,
- :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
- :wrappers => true,
- :build_args => nil,
+ :prerelease => false,
+ :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
+ :wrappers => true,
+ :build_args => nil,
:build_docs_in_background => false,
:install_as_default => false,
}.freeze
@@ -230,22 +231,22 @@ class Gem::DependencyInstaller
@installed_gems = []
options = {
- :bin_dir => @bin_dir,
- :build_args => @build_args,
- :document => @document,
- :env_shebang => @env_shebang,
- :force => @force,
- :format_executable => @format_executable,
+ :bin_dir => @bin_dir,
+ :build_args => @build_args,
+ :document => @document,
+ :env_shebang => @env_shebang,
+ :force => @force,
+ :format_executable => @format_executable,
:ignore_dependencies => @ignore_dependencies,
- :prerelease => @prerelease,
- :security_policy => @security_policy,
- :user_install => @user_install,
- :wrappers => @wrappers,
- :build_root => @build_root,
- :install_as_default => @install_as_default,
- :dir_mode => @dir_mode,
- :data_mode => @data_mode,
- :prog_mode => @prog_mode,
+ :prerelease => @prerelease,
+ :security_policy => @security_policy,
+ :user_install => @user_install,
+ :wrappers => @wrappers,
+ :build_root => @build_root,
+ :install_as_default => @install_as_default,
+ :dir_mode => @dir_mode,
+ :data_mode => @data_mode,
+ :prog_mode => @prog_mode,
}
options[:install_dir] = @install_dir if @only_install_dir
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index eaf6702177..5820298bb9 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
index 5fe0afb6b0..9fff306949 100644
--- a/lib/rubygems/deprecate.rb
+++ b/lib/rubygems/deprecate.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Provides 3 methods for declaring when something is going away.
#
@@ -143,7 +144,7 @@ module Gem::Deprecate
end
# Deprecation method to deprecate Rubygems commands
- def rubygems_deprecate_command
+ def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version)
class_eval do
define_method "deprecated?" do
true
@@ -151,7 +152,7 @@ module Gem::Deprecate
define_method "deprecation_warning" do
msg = [ "#{self.command} command is deprecated",
- ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}.\n",
+ ". It will be removed in Rubygems #{version}.\n",
]
alert_warning "#{msg.join}" unless Gem::Deprecate.skip
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index 96829227fc..1d3889af77 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "user_interaction"
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index ac82a551a5..b36fabffc2 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# This file contains all the various exceptions and other errors that are used
# inside of RubyGems.
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index ca4fbb20de..b92396f0ef 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -214,6 +214,16 @@ class Gem::RubyVersionMismatch < Gem::Exception; end
class Gem::VerificationError < Gem::Exception; end
##
+# Raised by Gem::WebauthnListener when an error occurs during security
+# device verification.
+
+class Gem::WebauthnVerificationError < Gem::Exception
+ def initialize(message)
+ super "Security device verification failed: #{message}"
+ end
+end
+
+##
# Raised to indicate that a system exit should occur with the specified
# exit_code
diff --git a/lib/rubygems/ext.rb b/lib/rubygems/ext.rb
index d714985c21..b5ca126a08 100644
--- a/lib/rubygems/ext.rb
+++ b/lib/rubygems/ext.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/ext/build_error.rb b/lib/rubygems/ext/build_error.rb
index 727bc065c2..0329c1eec3 100644
--- a/lib/rubygems/ext/build_error.rb
+++ b/lib/rubygems/ext/build_error.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Raised when there is an error while building extensions.
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index b46f9b5cd5..4960ddd7b1 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -6,6 +7,7 @@
#++
require_relative "../user_interaction"
+require_relative "../shellwords"
class Gem::Ext::Builder
include Gem::UserInteraction
@@ -17,7 +19,7 @@ class Gem::Ext::Builder
$1.downcase
end
- def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil)
+ def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = ["clean", "", "install"])
unless File.exist? File.join(make_dir, "Makefile")
raise Gem::InstallError, "Makefile not found"
end
@@ -40,7 +42,7 @@ class Gem::Ext::Builder
env << "sitelibdir=%s" % sitedir
end
- ["clean", "", "install"].each do |target|
+ targets.each do |target|
# Pass DESTDIR via command line to override what's in MAKEFLAGS
cmd = [
*make_program,
@@ -55,6 +57,22 @@ class Gem::Ext::Builder
end
end
+ def self.ruby
+ # Gem.ruby is quoted if it contains whitespace
+ cmd = Shellwords.split(Gem.ruby)
+
+ # This load_path is only needed when running rubygems test without a proper installation.
+ # Prepending it in a normal installation will cause problem with order of $LOAD_PATH.
+ # Therefore only add load_path if it is not present in the default $LOAD_PATH.
+ load_path = File.expand_path("../..", __dir__)
+ case load_path
+ when RbConfig::CONFIG["sitelibdir"], RbConfig::CONFIG["vendorlibdir"], RbConfig::CONFIG["rubylibdir"]
+ cmd
+ else
+ cmd << "-I#{load_path}"
+ end
+ end
+
def self.run(command, results, command_name = nil, dir = Dir.pwd, env = {})
verbose = Gem.configuration.really_verbose
@@ -65,8 +83,7 @@ class Gem::Ext::Builder
p(command)
end
results << "current directory: #{dir}"
- require "shellwords"
- results << command.shelljoin
+ results << Shellwords.join(command)
require "open3"
# Set $SOURCE_DATE_EPOCH for the subprocess.
@@ -131,8 +148,7 @@ class Gem::Ext::Builder
when /CMakeLists.txt/ then
Gem::Ext::CmakeBuilder
when /Cargo.toml/ then
- # We use the spec name here to ensure we invoke the correct init function later
- Gem::Ext::CargoBuilder.new(@spec)
+ Gem::Ext::CargoBuilder.new
else
build_error("No builder for extension '#{extension}'")
end
diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb
index e33b07a8a2..ce3b296f79 100644
--- a/lib/rubygems/ext/cargo_builder.rb
+++ b/lib/rubygems/ext/cargo_builder.rb
@@ -1,35 +1,67 @@
# frozen_string_literal: true
+require_relative "../shellwords"
+
# This class is used by rubygems to build Rust extensions. It is a thin-wrapper
# over the `cargo rustc` command which takes care of building Rust code in a way
# that Ruby can use.
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
attr_accessor :spec, :runner, :profile
- def initialize(spec)
+ def initialize
require_relative "../command"
require_relative "cargo_builder/link_flag_converter"
- @spec = spec
@runner = self.class.method(:run)
@profile = :release
end
- def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
+ def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
+ require "tempfile"
require "fileutils"
- require "shellwords"
- build_crate(dest_path, results, args, cargo_dir)
- validate_cargo_build!(dest_path)
- rename_cdylib_for_ruby_compatibility(dest_path)
- finalize_directory(dest_path, lib_dir, cargo_dir)
- results
- end
+ # Where's the Cargo.toml of the crate we're building
+ cargo_toml = File.join(cargo_dir, "Cargo.toml")
+ # What's the crate's name
+ crate_name = cargo_crate_name(cargo_dir, cargo_toml, results)
+
+ begin
+ # Create a tmp dir to do the build in
+ tmp_dest = Dir.mktmpdir(".gem.", cargo_dir)
+
+ # Run the build
+ cmd = cargo_command(cargo_toml, tmp_dest, args, crate_name)
+ runner.call(cmd, results, "cargo", cargo_dir, build_env)
+
+ # Where do we expect Cargo to write the compiled library
+ dylib_path = cargo_dylib_path(tmp_dest, crate_name)
+
+ # Helpful error if we didn't find the compiled library
+ raise DylibNotFoundError, tmp_dest unless File.exist?(dylib_path)
+
+ # Cargo and Ruby differ on how the library should be named, rename from
+ # what Cargo outputs to what Ruby expects
+ dlext_name = "#{crate_name}.#{makefile_config("DLEXT")}"
+ dlext_path = File.join(File.dirname(dylib_path), dlext_name)
+ FileUtils.cp(dylib_path, dlext_path)
- def build_crate(dest_path, results, args, cargo_dir)
- env = build_env
- cmd = cargo_command(cargo_dir, dest_path, args)
- runner.call cmd, results, "cargo", cargo_dir, env
+ nesting = extension_nesting(extension)
+
+ # TODO: remove in RubyGems 4
+ if Gem.install_extension_in_lib && lib_dir
+ nested_lib_dir = File.join(lib_dir, nesting)
+ FileUtils.mkdir_p nested_lib_dir
+ FileUtils.cp_r dlext_path, nested_lib_dir, remove_destination: true
+ end
+
+ # move to final destination
+ nested_dest_path = File.join(dest_path, nesting)
+ FileUtils.mkdir_p nested_dest_path
+ FileUtils.cp_r dlext_path, nested_dest_path, remove_destination: true
+ ensure
+ # clean up intermediary build artifacts
+ FileUtils.rm_rf tmp_dest if tmp_dest
+ end
results
end
@@ -37,44 +69,64 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
def build_env
build_env = rb_config_env
build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
+ cfg = "--cfg=rb_sys_gem --cfg=rubygems --cfg=rubygems_#{Gem::VERSION.tr(".", "_")}"
+ build_env["RUSTFLAGS"] = [ENV["RUSTFLAGS"], cfg].compact.join(" ")
build_env
end
- def cargo_command(cargo_dir, dest_path, args = [])
- manifest = File.join(cargo_dir, "Cargo.toml")
- cargo = ENV.fetch("CARGO", "cargo")
-
+ def cargo_command(cargo_toml, dest_path, args = [], crate_name = nil)
cmd = []
cmd += [cargo, "rustc"]
+ cmd += ["--crate-type", "cdylib"]
cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
cmd += ["--target-dir", dest_path]
- cmd += ["--manifest-path", manifest]
+ cmd += ["--manifest-path", cargo_toml]
cmd += ["--lib"]
cmd += ["--profile", profile.to_s]
- cmd += ["--locked"] if profile == :release
+ cmd += ["--locked"]
cmd += Gem::Command.build_args
cmd += args
cmd += ["--"]
- cmd += [*cargo_rustc_args(dest_path)]
+ cmd += [*cargo_rustc_args(dest_path, crate_name)]
cmd
end
private
+ def cargo
+ ENV.fetch("CARGO", "cargo")
+ end
+
+ # returns the directory nesting of the extension, ignoring the first part, so
+ # "ext/foo/bar/Cargo.toml" becomes "foo/bar"
+ def extension_nesting(extension)
+ parts = extension.to_s.split(Regexp.union([File::SEPARATOR, File::ALT_SEPARATOR].compact))
+
+ parts = parts.each_with_object([]) do |segment, final|
+ next if segment == "."
+ if segment == ".."
+ raise Gem::InstallError, "extension outside of gem root" if final.empty?
+ next final.pop
+ end
+ final << segment
+ end
+
+ File.join(parts[1...-1])
+ end
+
def rb_config_env
result = {}
RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v }
result
end
- def cargo_rustc_args(dest_dir)
+ def cargo_rustc_args(dest_dir, crate_name)
[
*linker_args,
*mkmf_libpath,
- *rustc_dynamic_linker_flags(dest_dir),
+ *rustc_dynamic_linker_flags(dest_dir, crate_name),
*rustc_lib_flags(dest_dir),
*platform_specific_rustc_args(dest_dir),
- *debug_flags,
]
end
@@ -92,6 +144,9 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# run on one that isn't the missing libraries will cause the extension
# to fail on start.
flags += ["-C", "link-arg=-static-libgcc"]
+ elsif darwin_target?
+ # Ventura does not always have this flag enabled
+ flags += ["-C", "link-arg=-Wl,-undefined,dynamic_lookup"]
end
flags
@@ -100,14 +155,23 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# We want to use the same linker that Ruby uses, so that the linker flags from
# mkmf work properly.
def linker_args
- # Have to handle CC="cl /nologo" on mswin
cc_flag = Shellwords.split(makefile_config("CC"))
linker = cc_flag.shift
link_args = cc_flag.flat_map {|a| ["-C", "link-arg=#{a}"] }
+ return mswin_link_args if linker == "cl"
+
["-C", "linker=#{linker}", *link_args]
end
+ def mswin_link_args
+ args = []
+ args += ["-l", makefile_config("LIBRUBYARG_SHARED").chomp(".lib")]
+ args += split_flags("LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
+ args += split_flags("LOCAL_LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
+ args
+ end
+
def libruby_args(dest_dir)
libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
raw_libs = Shellwords.split(libs)
@@ -120,42 +184,70 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
makefile_config("ENABLE_SHARED") == "no"
end
- # Ruby expects the dylib to follow a file name convention for loading
- def rename_cdylib_for_ruby_compatibility(dest_path)
- new_path = final_extension_path(dest_path)
- FileUtils.cp(cargo_dylib_path(dest_path), new_path)
- new_path
+ def cargo_dylib_path(dest_path, crate_name)
+ prefix = so_ext == "dll" ? "" : "lib"
+ path_parts = [dest_path]
+ path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
+ path_parts += ["release", "#{prefix}#{crate_name}.#{so_ext}"]
+ File.join(*path_parts)
end
- def validate_cargo_build!(dir)
- dylib_path = cargo_dylib_path(dir)
+ def cargo_crate_name(cargo_dir, manifest_path, results)
+ require "open3"
+ Gem.load_yaml
- raise DylibNotFoundError, dir unless File.exist?(dylib_path)
+ output, status =
+ begin
+ Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", :chdir => cargo_dir)
+ rescue => error
+ raise Gem::InstallError, "cargo metadata failed #{error.message}"
+ end
- dylib_path
- end
+ unless status.success?
+ if Gem.configuration.really_verbose
+ puts output
+ else
+ results << output
+ end
- def final_extension_path(dest_path)
- dylib_path = cargo_dylib_path(dest_path)
- dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
- dylib_path.gsub(File.basename(dylib_path), dlext_name)
- end
+ exit_reason =
+ if status.exited?
+ ", exit code #{status.exitstatus}"
+ elsif status.signaled?
+ ", uncaught signal #{status.termsig}"
+ end
- def cargo_dylib_path(dest_path)
- prefix = so_ext == "dll" ? "" : "lib"
- path_parts = [dest_path]
- path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
- path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"]
- File.join(*path_parts)
+ raise Gem::InstallError, "cargo metadata failed#{exit_reason}"
+ end
+
+ # cargo metadata output is specified as json, but with the
+ # --format-version 1 option the output is compatible with YAML, so we can
+ # avoid the json dependency
+ metadata = Gem::SafeYAML.safe_load(output)
+ package = metadata["packages"].find {|pkg| normalize_path(pkg["manifest_path"]) == manifest_path }
+ unless package
+ found = metadata["packages"].map {|md| "#{md["name"]} at #{md["manifest_path"]}" }
+ raise Gem::InstallError, <<-EOF
+failed to determine cargo package name
+
+looking for: #{manifest_path}
+
+found:
+#{found.join("\n")}
+EOF
+ end
+ package["name"].tr("-", "_")
end
- def cargo_crate_name
- spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
+ def normalize_path(path)
+ return path unless File::ALT_SEPARATOR
+
+ path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
end
- def rustc_dynamic_linker_flags(dest_dir)
+ def rustc_dynamic_linker_flags(dest_dir, crate_name)
split_flags("DLDFLAGS")
- .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
+ .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir, crate_name) }
.compact
.flat_map {|arg| ldflag_to_link_modifier(arg) }
end
@@ -189,8 +281,8 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
!!Gem::WIN_PATTERNS.find {|r| target_platform =~ r }
end
- # Interpolate substition vars in the arg (i.e. $(DEFFILE))
- def maybe_resolve_ldflag_variable(input_arg, dest_dir)
+ # Interpolate substitution vars in the arg (i.e. $(DEFFILE))
+ def maybe_resolve_ldflag_variable(input_arg, dest_dir, crate_name)
var_matches = input_arg.match(/\$\((\w+)\)/)
return input_arg unless var_matches
@@ -203,19 +295,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# On windows, it is assumed that mkmf has setup an exports file for the
# extension, so we have to to create one ourselves.
when "DEFFILE"
- write_deffile(dest_dir)
+ write_deffile(dest_dir, crate_name)
else
RbConfig::CONFIG[var_name]
end
end
- def write_deffile(dest_dir)
- deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
+ def write_deffile(dest_dir, crate_name)
+ deffile_path = File.join(dest_dir, "#{crate_name}-#{RbConfig::CONFIG["arch"]}.def")
export_prefix = makefile_config("EXPORT_PREFIX") || ""
File.open(deffile_path, "w") do |f|
f.puts "EXPORTS"
- f.puts "#{export_prefix.strip}Init_#{spec.name}"
+ f.puts "#{export_prefix.strip}Init_#{crate_name}"
end
deffile_path
@@ -250,59 +342,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
RbConfig.expand(val.dup)
end
- # Good balance between binary size and debugability
- def debug_flags
- return [] if profile == :dev
-
- ["-C", "debuginfo=1"]
- end
-
- # Copied from ExtConfBuilder
- def finalize_directory(dest_path, lib_dir, extension_dir)
- require "fileutils"
- require "tempfile"
-
- ext_path = final_extension_path(dest_path)
-
- begin
- tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
-
- # Some versions of `mktmpdir` return absolute paths, which will break make
- # if the paths contain spaces.
- #
- # As such, we convert to a relative path.
- tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
-
- full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
-
- # TODO: remove in RubyGems 4
- if Gem.install_extension_in_lib && lib_dir
- FileUtils.mkdir_p lib_dir
- FileUtils.cp_r ext_path, lib_dir, remove_destination: true
- end
-
- FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
- destent = ent.class.new(dest_path, ent.rel)
- destent.exist? || FileUtils.mv(ent.path, destent.path)
- end
- ensure
- FileUtils.rm_rf tmp_dest if tmp_dest
- end
- end
-
- def get_relative_path(path, base)
- path[0..base.length - 1] = "." if path.start_with?(base)
- path
- end
-
- def profile_target_directory
- case profile
- when :release then "release"
- when :dev then "debug"
- else raise "unknown target directory for profile: #{profile}"
- end
- end
-
# Error raised when no cdylib artifact was created
class DylibNotFoundError < StandardError
def initialize(dir)
diff --git a/lib/rubygems/ext/cargo_builder/link_flag_converter.rb b/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
index 111bb05492..e4d196cb10 100644
--- a/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
+++ b/lib/rubygems/ext/cargo_builder/link_flag_converter.rb
@@ -3,20 +3,24 @@
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# Converts Ruby link flags into something cargo understands
class LinkFlagConverter
+ FILTERED_PATTERNS = [
+ /compress-debug-sections/, # Not supported by all linkers, and not required for Rust
+ ].freeze
+
def self.convert(arg)
+ return [] if FILTERED_PATTERNS.any? {|p| p.match?(arg) }
+
case arg.chomp
when /^-L\s*(.+)$/
["-L", "native=#{$1}"]
when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/
["-l", $1]
- when /^-l\s*:lib(\S+).a$/
- ["-l", "static=#{$1}"]
- when /^-l\s*:lib(\S+).(so|dylib|dll)$/
- ["-l", "dylib=#{$1}"]
+ when /^-l\s*([^:\s])+/ # -lfoo, but not -l:libfoo.a
+ ["-l", $1]
when /^-F\s*(.*)$/
["-l", "framework=#{$1}"]
else
- ["-C", "link_arg=#{arg}"]
+ ["-C", "link-args=#{arg}"]
end
end
end
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index 51106c6370..6b8590ba2e 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index ebe398f56c..9c22329fe3 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -21,8 +22,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
destdir = ENV["DESTDIR"]
begin
- require "shellwords"
- cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../..", __dir__) << File.basename(extension)
+ cmd = ruby << File.basename(extension)
cmd.push(*args)
run(cmd, results, class_name, extension_dir) do |s, r|
@@ -55,6 +55,8 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
destent = ent.class.new(dest_path, ent.rel)
destent.exist? || FileUtils.mv(ent.path, destent.path)
end
+
+ make dest_path, results, extension_dir, tmp_dest_relative, ["clean"]
ensure
ENV["DESTDIR"] = destdir
end
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 9f2e099d40..8f39a63e11 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+
+require_relative "../shellwords"
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -14,11 +17,10 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
rake = ENV["rake"]
if rake
- require "shellwords"
- rake = rake.shellsplit
+ rake = Shellwords.split(rake)
else
begin
- rake = [Gem.ruby, "-I#{File.expand_path("../..", __dir__)}", "-rrubygems", Gem.bin_path("rake", "rake")]
+ rake = ruby << "-rrubygems" << Gem.bin_path("rake", "rake")
rescue Gem::Exception
rake = [Gem.default_exec_format % "rake"]
end
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index 31890a60d7..d238a5863b 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index a785159196..92739617f6 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
+
require_relative "remote_fetcher"
require_relative "text"
+require_relative "gemcutter_utilities/webauthn_listener"
+require_relative "gemcutter_utilities/webauthn_poller"
##
# Utility methods for using the RubyGems API.
@@ -70,8 +73,7 @@ module Gem::GemcutterUtilities
@host ||=
begin
env_rubygems_host = ENV["RUBYGEMS_HOST"]
- env_rubygems_host = nil if
- env_rubygems_host && env_rubygems_host.empty?
+ env_rubygems_host = nil if env_rubygems_host&.empty?
env_rubygems_host || configured_host
end
@@ -82,7 +84,7 @@ module Gem::GemcutterUtilities
#
# If +allowed_push_host+ metadata is present, then it will only allow that host.
- def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, &block)
+ def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, credentials: {}, &block)
require "net/http"
self.host = host if host
@@ -105,7 +107,7 @@ module Gem::GemcutterUtilities
response = request_with_otp(method, uri, &block)
if mfa_unauthorized?(response)
- ask_otp
+ fetch_otp(credentials)
response = request_with_otp(method, uri, &block)
end
@@ -167,11 +169,12 @@ module Gem::GemcutterUtilities
mfa_params = get_mfa_params(profile)
all_params = scope_params.merge(mfa_params)
warning = profile["warning"]
+ credentials = { email: email, password: password }
say "#{warning}\n" if warning
response = rubygems_api_request(:post, "api/v1/api_key",
- sign_in_host, scope: scope) do |request|
+ sign_in_host, credentials: credentials, scope: scope) do |request|
request.basic_auth email, password
request["OTP"] = otp if otp
request.body = URI.encode_www_form({ name: key_name }.merge(all_params))
@@ -201,7 +204,8 @@ module Gem::GemcutterUtilities
# block was given or shows the response body to the user.
#
# If the response was not successful, shows an error to the user including
- # the +error_prefix+ and the response body.
+ # the +error_prefix+ and the response body. If the response was a permanent redirect,
+ # shows an error to the user including the redirect location.
def with_response(response, error_prefix = nil)
case response
@@ -211,6 +215,12 @@ module Gem::GemcutterUtilities
else
say clean_text(response.body)
end
+ when Net::HTTPPermanentRedirect, Net::HTTPRedirection then
+ message = "The request has redirected permanently to #{response['location']}. Please check your defined push host URL."
+ message = "#{error_prefix}: #{message}" if error_prefix
+
+ say clean_text(message)
+ terminate_interaction(ERROR_CODE)
else
message = response.body
message = "#{error_prefix}: #{message}" if error_prefix
@@ -243,9 +253,52 @@ module Gem::GemcutterUtilities
end
end
- def ask_otp
- say "You have enabled multi-factor authentication. Please enter OTP code."
- options[:otp] = ask "Code: "
+ def fetch_otp(credentials)
+ options[:otp] = if webauthn_url = webauthn_verification_url(credentials)
+ server = TCPServer.new 0
+ port = server.addr[1].to_s
+
+ url_with_port = "#{webauthn_url}?port=#{port}"
+ say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option."
+
+ threads = [WebauthnListener.listener_thread(host, server), WebauthnPoller.poll_thread(options, host, webauthn_url, credentials)]
+ otp_thread = wait_for_otp_thread(*threads)
+
+ threads.each(&:join)
+
+ if error = otp_thread[:error]
+ alert_error error.message
+ terminate_interaction(1)
+ end
+
+ say "You are verified with a security device. You may close the browser window."
+ otp_thread[:otp]
+ else
+ say "You have enabled multi-factor authentication. Please enter OTP code."
+ ask "Code: "
+ end
+ end
+
+ def wait_for_otp_thread(*threads)
+ loop do
+ threads.each do |otp_thread|
+ return otp_thread unless otp_thread.alive?
+ end
+ sleep 0.1
+ end
+ ensure
+ threads.each(&:exit)
+ end
+
+ def webauthn_verification_url(credentials)
+ response = rubygems_api_request(:post, "api/v1/webauthn_verification") do |request|
+ if credentials.empty?
+ request.add_field "Authorization", api_key
+ else
+ request.basic_auth credentials[:email], credentials[:password]
+ end
+ end
+ response.is_a?(Net::HTTPSuccess) ? response.body : nil
end
def pretty_host(host)
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
new file mode 100644
index 0000000000..bea9d9e397
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require_relative "webauthn_listener/response"
+
+##
+# The WebauthnListener class retrieves an OTP after a user successfully WebAuthns with the Gem host.
+# An instance opens a socket using the TCPServer instance given and listens for a request from the Gem host.
+# The request should be a GET request to the root path and contains the OTP code in the form
+# of a query parameter `code`. The listener will return the code which will be used as the OTP for
+# API requests.
+#
+# Types of responses sent by the listener after receiving a request:
+# - 200 OK: OTP code was successfully retrieved
+# - 204 No Content: If the request was an OPTIONS request
+# - 400 Bad Request: If the request did not contain a query parameter `code`
+# - 404 Not Found: The request was not to the root path
+# - 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request
+#
+# Example usage:
+#
+# thread = Gem::WebauthnListener.listener_thread("https://rubygems.example", server)
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ attr_reader :host
+
+ def initialize(host)
+ @host = host
+ end
+
+ def self.listener_thread(host, server)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(host).wait_for_otp_code(server)
+ rescue Gem::WebauthnVerificationError => e
+ thread[:error] = e
+ ensure
+ server.close
+ end
+ end
+
+ def wait_for_otp_code(server)
+ loop do
+ socket = server.accept
+ request_line = socket.gets
+
+ method, req_uri, _protocol = request_line.split(" ")
+ req_uri = URI.parse(req_uri)
+
+ responder = SocketResponder.new(socket)
+
+ unless root_path?(req_uri)
+ responder.send(NotFoundResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Page at #{req_uri.path} not found."
+ end
+
+ case method.upcase
+ when "OPTIONS"
+ responder.send(NoContentResponse.for(host))
+ next # will be GET
+ when "GET"
+ if otp = parse_otp_from_uri(req_uri)
+ responder.send(OkResponse.for(host))
+ return otp
+ end
+ responder.send(BadRequestResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Did not receive OTP from #{host}."
+ else
+ responder.send(MethodNotAllowedResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
+ end
+ end
+ end
+
+ private
+
+ def root_path?(uri)
+ uri.path == "/"
+ end
+
+ def parse_otp_from_uri(uri)
+ require "cgi"
+
+ return if uri.query.nil?
+ CGI.parse(uri.query).dig("code", 0)
+ end
+
+ class SocketResponder
+ def initialize(socket)
+ @socket = socket
+ end
+
+ def send(response)
+ @socket.print response.to_s
+ @socket.close
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
new file mode 100644
index 0000000000..7709a8fef3
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnListener Response class is used by the WebauthnListener to create
+# responses to be sent to the Gem host. It creates a Net::HTTPResponse instance
+# when initialized and can be converted to the appropriate format to be sent by a socket using `to_s`.
+# Net::HTTPResponse instances cannot be directly sent over a socket.
+#
+# Types of response classes:
+# - OkResponse
+# - NoContentResponse
+# - BadRequestResponse
+# - NotFoundResponse
+# - MethodNotAllowedResponse
+#
+# Example usage:
+#
+# server = TCPServer.new(0)
+# socket = server.accept
+#
+# response = OkResponse.for("https://rubygems.example")
+# socket.print response.to_s
+# socket.close
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ class Response
+ attr_reader :http_response
+
+ def self.for(host)
+ new(host)
+ end
+
+ def initialize(host)
+ @host = host
+
+ build_http_response
+ end
+
+ def to_s
+ status_line = "HTTP/#{@http_response.http_version} #{@http_response.code} #{@http_response.message}\r\n"
+ headers = @http_response.to_hash.map {|header, value| "#{header}: #{value.join(", ")}\r\n" }.join + "\r\n"
+ body = @http_response.body ? "#{@http_response.body}\n" : ""
+
+ status_line + headers + body
+ end
+
+ private
+
+ # Must be implemented in subclasses
+ def code
+ raise NotImplementedError
+ end
+
+ def reason_phrase
+ raise NotImplementedError
+ end
+
+ def body; end
+
+ def build_http_response
+ response_class = Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
+ @http_response = response_class.new("1.1", code, reason_phrase)
+ @http_response.instance_variable_set(:@read, true)
+
+ add_connection_header
+ add_access_control_headers
+ add_body
+ end
+
+ def add_connection_header
+ @http_response["connection"] = "close"
+ end
+
+ def add_access_control_headers
+ @http_response["access-control-allow-origin"] = @host
+ @http_response["access-control-allow-methods"] = "POST"
+ @http_response["access-control-allow-headers"] = %w[Content-Type Authorization x-csrf-token]
+ end
+
+ def add_body
+ return unless body
+ @http_response["content-type"] = "text/plain; charset=utf-8"
+ @http_response["content-length"] = body.bytesize
+ @http_response.instance_variable_set(:@body, body)
+ end
+ end
+
+ class OkResponse < Response
+ private
+
+ def code
+ 200
+ end
+
+ def reason_phrase
+ "OK"
+ end
+
+ def body
+ "success"
+ end
+ end
+
+ class NoContentResponse < Response
+ private
+
+ def code
+ 204
+ end
+
+ def reason_phrase
+ "No Content"
+ end
+ end
+
+ class BadRequestResponse < Response
+ private
+
+ def code
+ 400
+ end
+
+ def reason_phrase
+ "Bad Request"
+ end
+
+ def body
+ "missing code parameter"
+ end
+ end
+
+ class NotFoundResponse < Response
+ private
+
+ def code
+ 404
+ end
+
+ def reason_phrase
+ "Not Found"
+ end
+ end
+
+ class MethodNotAllowedResponse < Response
+ private
+
+ def code
+ 405
+ end
+
+ def reason_phrase
+ "Method Not Allowed"
+ end
+
+ def add_access_control_headers
+ super
+ @http_response["allow"] = %w[GET OPTIONS]
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_poller.rb b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
new file mode 100644
index 0000000000..e7068605a4
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnPoller class retrieves an OTP after a user successfully WebAuthns. An instance
+# polls the Gem host for the OTP code. The polling request (api/v1/webauthn_verification/<webauthn_token>/status.json)
+# is sent to the Gem host every 5 seconds and will timeout after 5 minutes. If the status field in the json response
+# is "success", the code field will contain the OTP code.
+#
+# Example usage:
+#
+# thread = Gem::WebauthnPoller.poll_thread(
+# {},
+# "RubyGems.org",
+# "https://rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY",
+# { email: "email@example.com", password: "password" }
+# )
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnPoller
+ include Gem::GemcutterUtilities
+ TIMEOUT_IN_SECONDS = 300
+
+ attr_reader :options, :host
+
+ def initialize(options, host)
+ @options = options
+ @host = host
+ end
+
+ def self.poll_thread(options, host, webauthn_url, credentials)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(options, host).poll_for_otp(webauthn_url, credentials)
+ rescue Gem::WebauthnVerificationError, Timeout::Error => e
+ thread[:error] = e
+ end
+ end
+
+ def poll_for_otp(webauthn_url, credentials)
+ Timeout.timeout(TIMEOUT_IN_SECONDS) do
+ loop do
+ response = webauthn_verification_poll_response(webauthn_url, credentials)
+ raise Gem::WebauthnVerificationError, response.message unless response.is_a?(Net::HTTPSuccess)
+
+ require "json"
+ parsed_response = JSON.parse(response.body)
+ case parsed_response["status"]
+ when "pending"
+ sleep 5
+ when "success"
+ return parsed_response["code"]
+ else
+ raise Gem::WebauthnVerificationError, parsed_response.fetch("message", "Invalid response from server")
+ end
+ end
+ end
+ end
+
+ private
+
+ def webauthn_verification_poll_response(webauthn_url, credentials)
+ webauthn_token = %r{(?<=\/)[^\/]+(?=$)}.match(webauthn_url)[0]
+ rubygems_api_request(:get, "api/v1/webauthn_verification/#{webauthn_token}/status.json") do |request|
+ if credentials.empty?
+ request.add_field "Authorization", api_key
+ else
+ request.basic_auth credentials[:email], credentials[:password]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 39653462e9..0935fe8486 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "package"
require "tmpdir"
@@ -397,7 +398,7 @@ class Gem::Indexer
dst_name = File.join @dest_directory, file # REFACTOR: duped above
FileUtils.mv src_name, dst_name, :verbose => verbose,
- :force => true
+ :force => true
File.utime newest_mtime, newest_mtime, dst_name
end
diff --git a/lib/rubygems/install_default_message.rb b/lib/rubygems/install_default_message.rb
index 0d112a15df..0640eaaf08 100644
--- a/lib/rubygems/install_default_message.rb
+++ b/lib/rubygems/install_default_message.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "user_interaction"
diff --git a/lib/rubygems/install_message.rb b/lib/rubygems/install_message.rb
index 2565f36261..a24e26b918 100644
--- a/lib/rubygems/install_message.rb
+++ b/lib/rubygems/install_message.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "user_interaction"
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 79effcf21f..e125b3b9b3 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 531ca8716e..1b152aaf1f 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -342,6 +343,8 @@ class Gem::Installer
Gem::Specification.add_spec(spec)
+ load_plugin
+
run_post_install_hooks
spec
@@ -388,7 +391,7 @@ class Gem::Installer
# we'll be installing into.
def installed_specs
- @specs ||= begin
+ @installed_specs ||= begin
specs = []
Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
@@ -649,9 +652,9 @@ class Gem::Installer
def process_options # :nodoc:
@options = {
- :bin_dir => nil,
- :env_shebang => false,
- :force => false,
+ :bin_dir => nil,
+ :env_shebang => false,
+ :force => false,
:only_install_dir => false,
:post_install_message => true,
}.merge options
@@ -1002,4 +1005,17 @@ TEXT
""
end
end
+
+ def load_plugin
+ specs = Gem::Specification.find_all_by_name(spec.name)
+ # If old version already exists, this plugin isn't loaded
+ # immediately. It's for avoiding a case that multiple versions
+ # are loaded at the same time.
+ return unless specs.size == 1
+
+ plugin_files = spec.plugins.map do |plugin|
+ File.join(@plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}")
+ end
+ Gem.load_plugin_files(plugin_files)
+ end
end
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index b2c2dea905..3d970ef225 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/mock_gem_ui.rb b/lib/rubygems/mock_gem_ui.rb
index 5cc67ad099..2dfdc6d523 100644
--- a/lib/rubygems/mock_gem_ui.rb
+++ b/lib/rubygems/mock_gem_ui.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "user_interaction"
##
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index 767dc1fb45..c26d0765e6 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
#
# Represents a gem of name +name+ at +version+ of +platform+. These
diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb
index 98865612ba..1e50bda769 100644
--- a/lib/rubygems/optparse/lib/optparse.rb
+++ b/lib/rubygems/optparse/lib/optparse.rb
@@ -425,7 +425,7 @@
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class Gem::OptionParser
- Gem::OptionParser::Version = "0.2.0"
+ Gem::OptionParser::Version = "0.3.0"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -765,15 +765,15 @@ class Gem::OptionParser
end
#
- # Switch that takes an argument, which does not begin with '-'.
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
#
class PlacedArgument < self
#
- # Returns nil if argument is not present or begins with '-'.
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
#
def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
return nil, block, nil
end
opt = (val = parse_arg(val, &error))[1]
@@ -1148,6 +1148,7 @@ XXX
@summary_indent = indent
@default_argv = ARGV
@require_exact = false
+ @raise_unknown = true
add_officious
yield self if block_given?
end
@@ -1225,6 +1226,9 @@ XXX
# abbreviated long option as short option).
attr_accessor :require_exact
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
#
# Heading banner preceding summary.
#
@@ -1502,7 +1506,7 @@ XXX
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
- desc.push(o)
+ desc.push(o) if o && !o.empty?
end
end
@@ -1639,9 +1643,11 @@ XXX
begin
sw, = complete(:long, opt, true)
if require_exact && !sw.long.include?(arg)
+ throw :terminate, arg unless raise_unknown
raise InvalidOption, arg
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1673,6 +1679,7 @@ XXX
end
end
rescue ParseError
+ throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
@@ -1862,12 +1869,7 @@ XXX
end
all_candidates.select! {|cand| cand.is_a?(String) }
checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
- suggestions = all_candidates & checker.correct(opt)
- if DidYouMean.respond_to?(:formatter)
- DidYouMean.formatter.message_for(suggestions)
- else
- "\nDid you mean? #{suggestions.join("\n ")}"
- end
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
end
def candidate(word)
@@ -1908,10 +1910,13 @@ XXX
# directory ~/.options, then the basename with '.options' suffix
# under XDG and Haiku standard places.
#
- def load(filename = nil)
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, into: nil)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options')) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
basename << ".options"
return [
# XDG
@@ -1923,11 +1928,11 @@ XXX
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir)) rescue nil
+ load(File.expand_path(basename, dir), into: into) rescue nil
}
end
begin
- parse(*IO.readlines(filename).each {|s| s.chomp!})
+ parse(*File.readlines(filename, chomp: true), into: into)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 084dc5d2d9..f28d521bdf 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -444,10 +445,10 @@ EOM
directories << mkdir
end
- File.open destination, "wb" do |out|
- out.write entry.read
+ if entry.file?
+ File.open(destination, "wb") {|out| out.write entry.read }
FileUtils.chmod file_mode(entry.header.mode), destination
- end if entry.file?
+ end
verbose destination
end
@@ -467,7 +468,12 @@ EOM
end
def file_mode(mode) # :nodoc:
- ((mode & 0111).zero? ? data_mode : prog_mode) || mode
+ ((mode & 0111).zero? ? data_mode : prog_mode) ||
+ # If we're not using one of the default modes, then we're going to fall
+ # back to the mode from the tarball. In this case we need to mask it down
+ # to fit into 2^16 bits (the maximum value for a mode in CRuby since it
+ # gets put into an unsigned short).
+ (mode & ((1 << 16) - 1))
end
##
@@ -611,8 +617,7 @@ EOM
verify_checksums @digests, @checksums
- @security_policy.verify_signatures @spec, @digests, @signatures if
- @security_policy
+ @security_policy&.verify_signatures @spec, @digests, @signatures
true
rescue Gem::Security::Exception
diff --git a/lib/rubygems/package/digest_io.rb b/lib/rubygems/package/digest_io.rb
index 4736f76d93..1572cb9f2c 100644
--- a/lib/rubygems/package/digest_io.rb
+++ b/lib/rubygems/package/digest_io.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# IO wrapper that creates digests of contents written to the IO it wraps.
diff --git a/lib/rubygems/package/file_source.rb b/lib/rubygems/package/file_source.rb
index 14c7a9f6d2..d9717e0f2a 100644
--- a/lib/rubygems/package/file_source.rb
+++ b/lib/rubygems/package/file_source.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The primary source of gems is a file on disk, including all usages
# internal to rubygems.
diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb
index 03d7714524..227835dfce 100644
--- a/lib/rubygems/package/io_source.rb
+++ b/lib/rubygems/package/io_source.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Supports reading and writing gems from/to a generic IO object. This is
# useful for other applications built on top of rubygems, such as
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 09a02d3ecd..bf0ed61b0a 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/package/source.rb b/lib/rubygems/package/source.rb
index 69701e55e9..8c44f8c305 100644
--- a/lib/rubygems/package/source.rb
+++ b/lib/rubygems/package/source.rb
@@ -1,3 +1,4 @@
# frozen_string_literal: true
+
class Gem::Package::Source # :nodoc:
end
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
index ee515a9e05..99bd755f90 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -103,22 +104,22 @@ class Gem::Package::TarHeader
fields = header.unpack UNPACK_FORMAT
- new :name => fields.shift,
- :mode => strict_oct(fields.shift),
- :uid => oct_or_256based(fields.shift),
- :gid => oct_or_256based(fields.shift),
- :size => strict_oct(fields.shift),
- :mtime => strict_oct(fields.shift),
+ new :name => fields.shift,
+ :mode => strict_oct(fields.shift),
+ :uid => oct_or_256based(fields.shift),
+ :gid => oct_or_256based(fields.shift),
+ :size => strict_oct(fields.shift),
+ :mtime => strict_oct(fields.shift),
:checksum => strict_oct(fields.shift),
:typeflag => fields.shift,
:linkname => fields.shift,
- :magic => fields.shift,
- :version => strict_oct(fields.shift),
- :uname => fields.shift,
- :gname => fields.shift,
+ :magic => fields.shift,
+ :version => strict_oct(fields.shift),
+ :uname => fields.shift,
+ :gname => fields.shift,
:devmajor => strict_oct(fields.shift),
:devminor => strict_oct(fields.shift),
- :prefix => fields.shift,
+ :prefix => fields.shift,
:empty => empty
end
@@ -208,7 +209,7 @@ class Gem::Package::TarHeader
private
def calculate_checksum(header)
- header.unpack("C*").inject {|a, b| a + b }
+ header.sum(0)
end
def header(checksum = @checksum)
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index cdc3fdc015..a8dd39c572 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -53,39 +54,11 @@ class Gem::Package::TarReader
def each
return enum_for __method__ unless block_given?
- use_seek = @io.respond_to?(:seek)
-
until @io.eof? do
header = Gem::Package::TarHeader.from @io
return if header.empty?
-
entry = Gem::Package::TarReader::Entry.new header, @io
- size = entry.header.size
-
yield entry
-
- skip = (512 - (size % 512)) % 512
- pending = size - entry.bytes_read
-
- if use_seek
- begin
- # avoid reading if the @io supports seeking
- @io.seek pending, IO::SEEK_CUR
- pending = 0
- rescue Errno::EINVAL
- end
- end
-
- # if seeking isn't supported or failed
- while pending > 0 do
- bytes_read = @io.read([pending, 4096].min).size
- raise UnexpectedEOF if @io.eof?
- pending -= bytes_read
- end
-
- @io.read skip # discard trailing zeros
-
- # make sure nobody can use #read, #getc or #rewind anymore
entry.close
end
end
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index 8634381c18..e770079f41 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#++
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
@@ -9,6 +10,20 @@
class Gem::Package::TarReader::Entry
##
+ # Creates a new tar entry for +header+ that will be read from +io+
+ # If a block is given, the entry is yielded and then closed.
+
+ def self.open(header, io, &block)
+ entry = new header, io
+ return entry unless block_given?
+ begin
+ yield entry
+ ensure
+ entry.close
+ end
+ end
+
+ ##
# Header for this tar entry
attr_reader :header
@@ -21,6 +36,7 @@ class Gem::Package::TarReader::Entry
@header = header
@io = io
@orig_pos = @io.pos
+ @end_pos = @orig_pos + @header.size
@read = 0
end
@@ -39,7 +55,14 @@ class Gem::Package::TarReader::Entry
# Closes the tar entry
def close
+ return if closed?
+ # Seek to the end of the entry if it wasn't fully read
+ seek(0, IO::SEEK_END)
+ # discard trailing zeros
+ skip = (512 - (@header.size % 512)) % 512
+ @io.read(skip)
@closed = true
+ nil
end
##
@@ -117,6 +140,14 @@ class Gem::Package::TarReader::Entry
bytes_read
end
+ ##
+ # Seek to the position in the tar entry
+
+ def pos=(new_pos)
+ seek(new_pos, IO::SEEK_SET)
+ new_pos
+ end
+
def size
@header.size
end
@@ -130,9 +161,10 @@ class Gem::Package::TarReader::Entry
def read(len = nil)
check_closed
- return nil if @read >= @header.size
-
len ||= @header.size - @read
+
+ return nil if len > 0 && @read >= @header.size
+
max_read = [len, @header.size - @read].min
ret = @io.read max_read
@@ -144,9 +176,10 @@ class Gem::Package::TarReader::Entry
def readpartial(maxlen = nil, outbuf = "".b)
check_closed
- raise EOFError if @read >= @header.size
-
maxlen ||= @header.size - @read
+
+ raise EOFError if maxlen > 0 && @read >= @header.size
+
max_read = [maxlen, @header.size - @read].min
@io.readpartial(max_read, outbuf)
@@ -156,12 +189,61 @@ class Gem::Package::TarReader::Entry
end
##
+ # Seeks to +offset+ bytes into the tar file entry
+ # +whence+ can be IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END
+
+ def seek(offset, whence = IO::SEEK_SET)
+ check_closed
+
+ new_pos =
+ case whence
+ when IO::SEEK_SET then @orig_pos + offset
+ when IO::SEEK_CUR then @io.pos + offset
+ when IO::SEEK_END then @end_pos + offset
+ else
+ raise ArgumentError, "invalid whence"
+ end
+
+ if new_pos < @orig_pos
+ new_pos = @orig_pos
+ elsif new_pos > @end_pos
+ new_pos = @end_pos
+ end
+
+ pending = new_pos - @io.pos
+
+ if @io.respond_to?(:seek)
+ begin
+ # avoid reading if the @io supports seeking
+ @io.seek new_pos, IO::SEEK_SET
+ pending = 0
+ rescue Errno::EINVAL
+ end
+ end
+
+ # if seeking isn't supported or failed
+ # negative seek requires that we rewind and read
+ if pending < 0
+ @io.rewind
+ pending = new_pos
+ end
+
+ while pending > 0 do
+ size_read = @io.read([pending, 4096].min).size
+ raise UnexpectedEOF if @io.eof?
+ pending -= size_read
+ end
+
+ @read = @io.pos - @orig_pos
+
+ 0
+ end
+
+ ##
# Rewinds to the beginning of the tar file entry
def rewind
check_closed
-
- @io.pos = @orig_pos
- @read = 0
+ seek(0, IO::SEEK_SET)
end
end
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index db5242c5e4..32a13e9162 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index 8432bc5806..a67d8cb916 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
#
# Permission is hereby granted, free of charge, to any person obtaining
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index d601e653c9..d9df543ad9 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
#
# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index ed3571dbff..b721629b78 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "deprecate"
##
@@ -22,10 +23,11 @@ class Gem::Platform
end
def self.match_platforms?(platform, platforms)
+ platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
- (local_platform != Gem::Platform::RUBY && local_platform =~ platform)
+ (local_platform != Gem::Platform::RUBY && platform =~ local_platform)
end
end
private_class_method :match_platforms?
@@ -70,7 +72,7 @@ class Gem::Platform
when String then
arch = arch.split "-"
- if arch.length > 2 && arch.last !~ (/\d/) # reassemble x86-linux-gnu
+ if arch.length > 2 && arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end
@@ -96,13 +98,12 @@ class Gem::Platform
when /darwin(\d+)?/ then [ "darwin", $1 ]
when /^macruby$/ then [ "macruby", nil ]
when /freebsd(\d+)?/ then [ "freebsd", $1 ]
- when /hpux(\d+)?/ then [ "hpux", $1 ]
when /^java$/, /^jruby$/ then [ "java", nil ]
when /^java([\d.]*)/ then [ "java", $1 ]
when /^dalvik(\d+)?$/ then [ "dalvik", $1 ]
when /^dotnet$/ then [ "dotnet", nil ]
when /^dotnet([\d.]*)/ then [ "dotnet", $1 ]
- when /linux-?((?!gnu)\w+)?/ then [ "linux", $1 ]
+ when /linux-?(\w+)?/ then [ "linux", $1 ]
when /mingw32/ then [ "mingw32", nil ]
when /mingw-?(\w+)?/ then [ "mingw", $1 ]
when /(mswin\d+)(\_(\d+))?/ then
@@ -111,7 +112,6 @@ class Gem::Platform
[os, version]
when /netbsdelf/ then [ "netbsdelf", nil ]
when /openbsd(\d+\.\d+)?/ then [ "openbsd", $1 ]
- when /bitrig(\d+\.\d+)?/ then [ "bitrig", $1 ]
when /solaris(\d+\.\d+)?/ then [ "solaris", $1 ]
# test
when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
@@ -151,10 +151,20 @@ class Gem::Platform
##
# Does +other+ match this platform? Two platforms match if they have the
# same CPU, or either has a CPU of 'universal', they have the same OS, and
- # they have the same version, or either has no version.
+ # they have the same version, or either one has no version
#
# Additionally, the platform will match if the local CPU is 'arm' and the
# other CPU starts with "arm" (for generic ARM family support).
+ #
+ # Of note, this method is not commutative. Indeed the OS 'linux' has a
+ # special case: the version is the libc name, yet while "no version" stands
+ # as a wildcard for a binary gem platform (as for other OSes), for the
+ # runtime platform "no version" stands for 'gnu'. To be able to distinguish
+ # these, the method receiver is the gem platform, while the argument is
+ # the runtime platform.
+ #
+ #--
+ # NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
def ===(other)
return nil unless Gem::Platform === other
@@ -171,7 +181,23 @@ class Gem::Platform
@os == other.os &&
# version
- (@version.nil? || other.version.nil? || @version == other.version)
+ (
+ (@os != "linux" && (@version.nil? || other.version.nil?)) ||
+ (@os == "linux" && (normalized_linux_version == other.normalized_linux_version || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
+ @version == other.version
+ )
+ end
+
+ #--
+ # NOTE: Until it can be removed, changes to this method must also be reflected in `bundler/lib/bundler/rubygems_ext.rb`
+
+ def normalized_linux_version
+ return nil unless @version
+
+ without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
+ return nil if without_gnu_nor_abi_modifiers.empty?
+
+ without_gnu_nor_abi_modifiers
end
##
@@ -210,11 +236,11 @@ class Gem::Platform
# A pure-Ruby gem that may use Gem::Specification#extensions to build
# binary files.
- RUBY = "ruby".freeze
+ RUBY = "ruby"
##
# A platform-specific gem that is built for the packaging Ruby's platform.
# This will be replaced with Gem::Platform::local.
- CURRENT = "current".freeze
+ CURRENT = "current"
end
diff --git a/lib/rubygems/psych_tree.rb b/lib/rubygems/psych_tree.rb
index b90f9f7d1d..2d478c94d9 100644
--- a/lib/rubygems/psych_tree.rb
+++ b/lib/rubygems/psych_tree.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem
if defined? ::Psych::Visitors
class NoAliasYAMLTree < Psych::Visitors::YAMLTree
diff --git a/lib/rubygems/query_utils.rb b/lib/rubygems/query_utils.rb
index a502717f94..c72955f83b 100644
--- a/lib/rubygems/query_utils.rb
+++ b/lib/rubygems/query_utils.rb
@@ -151,7 +151,7 @@ module Gem::QueryUtils
fetcher.detect(specs_type) { true }
else
fetcher.detect(specs_type) do |name_tuple|
- name === name_tuple.name
+ name === name_tuple.name && options[:version].satisfied_by?(name_tuple.version)
end
end
@@ -159,7 +159,7 @@ module Gem::QueryUtils
end
def specs_type
- if options[:all]
+ if options[:all] || options[:version].specific?
if options[:prerelease]
:complete
else
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index 769ec61d1e..907dcd9431 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
begin
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 0ac6eaa130..1c8a441d0c 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../rubygems"
require_relative "request"
require_relative "request/connection_pools"
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index c3ea46e0eb..8ea39d9358 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require "net/http"
require_relative "user_interaction"
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
index 7b309eedd3..52543de41f 100644
--- a/lib/rubygems/request/http_pool.rb
+++ b/lib/rubygems/request/http_pool.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A connection "pool" that only manages one connection for now. Provides
# thread safe `checkout` and `checkin` methods. The pool consists of one
diff --git a/lib/rubygems/request/https_pool.rb b/lib/rubygems/request/https_pool.rb
index 50f42d9e0d..cb1d4b59b6 100644
--- a/lib/rubygems/request/https_pool.rb
+++ b/lib/rubygems/request/https_pool.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
private
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 64701a8214..b6e0995726 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "tsort"
##
@@ -107,7 +108,7 @@ class Gem::RequestSet
@requests = []
@sets = []
@soft_missing = false
- @sorted = nil
+ @sorted_requests = nil
@specs = nil
@vendor_set = nil
@source_set = nil
@@ -424,7 +425,7 @@ class Gem::RequestSet
end
def sorted_requests
- @sorted ||= strongly_connected_components.flatten
+ @sorted_requests ||= strongly_connected_components.flatten
end
def specs
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index fe75ac5208..2fd0da340a 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A semi-compatible DSL for the Bundler Gemfile and Isolate gem dependencies
# files.
@@ -32,22 +33,22 @@
class Gem::RequestSet::GemDependencyAPI
ENGINE_MAP = { # :nodoc:
- :jruby => %w[jruby],
- :jruby_18 => %w[jruby],
- :jruby_19 => %w[jruby],
- :maglev => %w[maglev],
- :mri => %w[ruby],
- :mri_18 => %w[ruby],
- :mri_19 => %w[ruby],
- :mri_20 => %w[ruby],
- :mri_21 => %w[ruby],
- :rbx => %w[rbx],
- :truffleruby => %w[truffleruby],
- :ruby => %w[ruby rbx maglev truffleruby],
- :ruby_18 => %w[ruby rbx maglev truffleruby],
- :ruby_19 => %w[ruby rbx maglev truffleruby],
- :ruby_20 => %w[ruby rbx maglev truffleruby],
- :ruby_21 => %w[ruby rbx maglev truffleruby],
+ :jruby => %w[jruby],
+ :jruby_18 => %w[jruby],
+ :jruby_19 => %w[jruby],
+ :maglev => %w[maglev],
+ :mri => %w[ruby],
+ :mri_18 => %w[ruby],
+ :mri_19 => %w[ruby],
+ :mri_20 => %w[ruby],
+ :mri_21 => %w[ruby],
+ :rbx => %w[rbx],
+ :truffleruby => %w[truffleruby],
+ :ruby => %w[ruby rbx maglev truffleruby],
+ :ruby_18 => %w[ruby rbx maglev truffleruby],
+ :ruby_19 => %w[ruby rbx maglev truffleruby],
+ :ruby_20 => %w[ruby rbx maglev truffleruby],
+ :ruby_21 => %w[ruby rbx maglev truffleruby],
}.freeze
mswin = Gem::Platform.new "x86-mswin32"
@@ -56,37 +57,37 @@ class Gem::RequestSet::GemDependencyAPI
x64_mingw = Gem::Platform.new "x64-mingw32"
PLATFORM_MAP = { # :nodoc:
- :jruby => Gem::Platform::RUBY,
- :jruby_18 => Gem::Platform::RUBY,
- :jruby_19 => Gem::Platform::RUBY,
- :maglev => Gem::Platform::RUBY,
- :mingw => x86_mingw,
- :mingw_18 => x86_mingw,
- :mingw_19 => x86_mingw,
- :mingw_20 => x86_mingw,
- :mingw_21 => x86_mingw,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mswin => mswin,
- :mswin_18 => mswin,
- :mswin_19 => mswin,
- :mswin_20 => mswin,
- :mswin_21 => mswin,
- :mswin64 => mswin64,
- :mswin64_19 => mswin64,
- :mswin64_20 => mswin64,
- :mswin64_21 => mswin64,
- :rbx => Gem::Platform::RUBY,
- :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :x64_mingw => x64_mingw,
+ :jruby => Gem::Platform::RUBY,
+ :jruby_18 => Gem::Platform::RUBY,
+ :jruby_19 => Gem::Platform::RUBY,
+ :maglev => Gem::Platform::RUBY,
+ :mingw => x86_mingw,
+ :mingw_18 => x86_mingw,
+ :mingw_19 => x86_mingw,
+ :mingw_20 => x86_mingw,
+ :mingw_21 => x86_mingw,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mswin => mswin,
+ :mswin_18 => mswin,
+ :mswin_19 => mswin,
+ :mswin_20 => mswin,
+ :mswin_21 => mswin,
+ :mswin64 => mswin64,
+ :mswin64_19 => mswin64,
+ :mswin64_20 => mswin64,
+ :mswin64_21 => mswin64,
+ :rbx => Gem::Platform::RUBY,
+ :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :truffleruby => Gem::Platform::RUBY,
+ :x64_mingw => x64_mingw,
:x64_mingw_20 => x64_mingw,
:x64_mingw_21 => x64_mingw,
}.freeze
@@ -98,68 +99,68 @@ class Gem::RequestSet::GemDependencyAPI
tilde_gt_2_1_0 = Gem::Requirement.new "~> 2.1.0"
VERSION_MAP = { # :nodoc:
- :jruby => gt_eq_0,
- :jruby_18 => tilde_gt_1_8_0,
- :jruby_19 => tilde_gt_1_9_0,
- :maglev => gt_eq_0,
- :mingw => gt_eq_0,
- :mingw_18 => tilde_gt_1_8_0,
- :mingw_19 => tilde_gt_1_9_0,
- :mingw_20 => tilde_gt_2_0_0,
- :mingw_21 => tilde_gt_2_1_0,
- :mri => gt_eq_0,
- :mri_18 => tilde_gt_1_8_0,
- :mri_19 => tilde_gt_1_9_0,
- :mri_20 => tilde_gt_2_0_0,
- :mri_21 => tilde_gt_2_1_0,
- :mswin => gt_eq_0,
- :mswin_18 => tilde_gt_1_8_0,
- :mswin_19 => tilde_gt_1_9_0,
- :mswin_20 => tilde_gt_2_0_0,
- :mswin_21 => tilde_gt_2_1_0,
- :mswin64 => gt_eq_0,
- :mswin64_19 => tilde_gt_1_9_0,
- :mswin64_20 => tilde_gt_2_0_0,
- :mswin64_21 => tilde_gt_2_1_0,
- :rbx => gt_eq_0,
- :ruby => gt_eq_0,
- :ruby_18 => tilde_gt_1_8_0,
- :ruby_19 => tilde_gt_1_9_0,
- :ruby_20 => tilde_gt_2_0_0,
- :ruby_21 => tilde_gt_2_1_0,
- :truffleruby => gt_eq_0,
- :x64_mingw => gt_eq_0,
+ :jruby => gt_eq_0,
+ :jruby_18 => tilde_gt_1_8_0,
+ :jruby_19 => tilde_gt_1_9_0,
+ :maglev => gt_eq_0,
+ :mingw => gt_eq_0,
+ :mingw_18 => tilde_gt_1_8_0,
+ :mingw_19 => tilde_gt_1_9_0,
+ :mingw_20 => tilde_gt_2_0_0,
+ :mingw_21 => tilde_gt_2_1_0,
+ :mri => gt_eq_0,
+ :mri_18 => tilde_gt_1_8_0,
+ :mri_19 => tilde_gt_1_9_0,
+ :mri_20 => tilde_gt_2_0_0,
+ :mri_21 => tilde_gt_2_1_0,
+ :mswin => gt_eq_0,
+ :mswin_18 => tilde_gt_1_8_0,
+ :mswin_19 => tilde_gt_1_9_0,
+ :mswin_20 => tilde_gt_2_0_0,
+ :mswin_21 => tilde_gt_2_1_0,
+ :mswin64 => gt_eq_0,
+ :mswin64_19 => tilde_gt_1_9_0,
+ :mswin64_20 => tilde_gt_2_0_0,
+ :mswin64_21 => tilde_gt_2_1_0,
+ :rbx => gt_eq_0,
+ :ruby => gt_eq_0,
+ :ruby_18 => tilde_gt_1_8_0,
+ :ruby_19 => tilde_gt_1_9_0,
+ :ruby_20 => tilde_gt_2_0_0,
+ :ruby_21 => tilde_gt_2_1_0,
+ :truffleruby => gt_eq_0,
+ :x64_mingw => gt_eq_0,
:x64_mingw_20 => tilde_gt_2_0_0,
:x64_mingw_21 => tilde_gt_2_1_0,
}.freeze
WINDOWS = { # :nodoc:
- :mingw => :only,
- :mingw_18 => :only,
- :mingw_19 => :only,
- :mingw_20 => :only,
- :mingw_21 => :only,
- :mri => :never,
- :mri_18 => :never,
- :mri_19 => :never,
- :mri_20 => :never,
- :mri_21 => :never,
- :mswin => :only,
- :mswin_18 => :only,
- :mswin_19 => :only,
- :mswin_20 => :only,
- :mswin_21 => :only,
- :mswin64 => :only,
- :mswin64_19 => :only,
- :mswin64_20 => :only,
- :mswin64_21 => :only,
- :rbx => :never,
- :ruby => :never,
- :ruby_18 => :never,
- :ruby_19 => :never,
- :ruby_20 => :never,
- :ruby_21 => :never,
- :x64_mingw => :only,
+ :mingw => :only,
+ :mingw_18 => :only,
+ :mingw_19 => :only,
+ :mingw_20 => :only,
+ :mingw_21 => :only,
+ :mri => :never,
+ :mri_18 => :never,
+ :mri_19 => :never,
+ :mri_20 => :never,
+ :mri_21 => :never,
+ :mswin => :only,
+ :mswin_18 => :only,
+ :mswin_19 => :only,
+ :mswin_20 => :only,
+ :mswin_21 => :only,
+ :mswin64 => :only,
+ :mswin64_19 => :only,
+ :mswin64_20 => :only,
+ :mswin64_21 => :only,
+ :rbx => :never,
+ :ruby => :never,
+ :ruby_18 => :never,
+ :ruby_19 => :never,
+ :ruby_20 => :never,
+ :ruby_21 => :never,
+ :x64_mingw => :only,
:x64_mingw_20 => :only,
:x64_mingw_21 => :only,
}.freeze
@@ -214,7 +215,7 @@ class Gem::RequestSet::GemDependencyAPI
git_source :github do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
- "git://github.com/#{repo_name}.git"
+ "https://github.com/#{repo_name}.git"
end
git_source :bitbucket do |repo_name|
@@ -435,7 +436,6 @@ Gem dependencies file #{@path} requires #{name} more than once.
reference ||= ref
reference ||= branch
reference ||= tag
- reference ||= "master"
if ref && branch
warn <<-WARNING
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 3ba202f661..9523fa7786 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Parses a gem.deps.rb.lock file and constructs a LockSet containing the
# dependencies found inside. If the lock file is missing no LockSet is
diff --git a/lib/rubygems/request_set/lockfile/parser.rb b/lib/rubygems/request_set/lockfile/parser.rb
index 8446f9df8e..1daec1fabd 100644
--- a/lib/rubygems/request_set/lockfile/parser.rb
+++ b/lib/rubygems/request_set/lockfile/parser.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::RequestSet::Lockfile::Parser
###
# Parses lockfiles
@@ -331,7 +332,7 @@ class Gem::RequestSet::Lockfile::Parser
set.find_all(requirement)
end.compact.first
- specification && specification.version
+ specification&.version
end
##
diff --git a/lib/rubygems/request_set/lockfile/tokenizer.rb b/lib/rubygems/request_set/lockfile/tokenizer.rb
index 4476a041c4..e91a11ca93 100644
--- a/lib/rubygems/request_set/lockfile/tokenizer.rb
+++ b/lib/rubygems/request_set/lockfile/tokenizer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#) frozen_string_literal: true
require_relative "parser"
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 4f19b8c5b0..eed12c4914 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "version"
##
@@ -10,19 +11,19 @@ require_relative "version"
class Gem::Requirement
OPS = { #:nodoc:
- "=" => lambda {|v, r| v == r },
- "!=" => lambda {|v, r| v != r },
- ">" => lambda {|v, r| v > r },
- "<" => lambda {|v, r| v < r },
- ">=" => lambda {|v, r| v >= r },
- "<=" => lambda {|v, r| v <= r },
- "~>" => lambda {|v, r| v >= r && v.release < r.bump },
+ "=" => lambda {|v, r| v == r },
+ "!=" => lambda {|v, r| v != r },
+ ">" => lambda {|v, r| v > r },
+ "<" => lambda {|v, r| v < r },
+ ">=" => lambda {|v, r| v >= r },
+ "<=" => lambda {|v, r| v <= r },
+ "~>" => lambda {|v, r| v >= r && v.release < r.bump },
}.freeze
SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc:
quoted = OPS.keys.map {|k| Regexp.quote k }.join "|"
- PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc:
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc:
##
# A regular expression that matches a requirement
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index bf7d6d943b..a912729b37 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "dependency"
require_relative "exceptions"
require_relative "util/list"
@@ -246,7 +247,7 @@ class Gem::Resolver
sources.each do |source|
groups[source].
- sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }.
+ sort_by {|spec| [spec.version, spec.platform =~ Gem::Platform.local ? 1 : 0] }.
map {|spec| ActivationRequest.new spec, dependency }.
each {|activation_request| activation_requests << activation_request }
end
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index 27877e0f4b..d59859c102 100644
--- a/lib/rubygems/resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Specifies a Specification object that should be activated. Also contains a
# dependency that was used to introduce this activation.
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index f2bef54a9c..9b57b03192 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The global rubygems pool, available via the rubygems.org API.
# Returns instances of APISpecification.
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 1e65d5e5a9..f26f82757e 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents a specification retrieved via the rubygems.org API.
#
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index 075ee1ef5c..d75fa7c00a 100644
--- a/lib/rubygems/resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The BestSet chooses the best available method to query a remote index.
#
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index 226da1e1e0..0991f0713e 100644
--- a/lib/rubygems/resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A ComposedSet allows multiple sets to be queried like a single set.
#
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index aba6d73ea7..dca41da51f 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Used internally to indicate that a dependency conflicted
# with a spec that would be activated.
diff --git a/lib/rubygems/resolver/current_set.rb b/lib/rubygems/resolver/current_set.rb
index c3aa3a2c37..370e445089 100644
--- a/lib/rubygems/resolver/current_set.rb
+++ b/lib/rubygems/resolver/current_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set which represents the installed gems. Respects
# all the normal settings that control where to look
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index 70a61cbc25..60b338277f 100644
--- a/lib/rubygems/resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Used Internally. Wraps a Dependency object to also track which spec
# contained the Dependency.
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
index f010273a8f..89342ff80d 100644
--- a/lib/rubygems/resolver/git_set.rb
+++ b/lib/rubygems/resolver/git_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A GitSet represents gems that are sourced from git repositories.
#
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
index 6a178ea82e..e587c17d2a 100644
--- a/lib/rubygems/resolver/git_specification.rb
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A GitSpecification represents a gem that is sourced from a git repository
# and is being loaded through a gem dependencies file through the +git:+
diff --git a/lib/rubygems/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
index 2344178314..5e8632c7d8 100644
--- a/lib/rubygems/resolver/index_set.rb
+++ b/lib/rubygems/resolver/index_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The global rubygems pool represented via the traditional
# source index.
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index 0fc758dfd3..6fac8c1487 100644
--- a/lib/rubygems/resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents a possible Specification object returned from IndexSet. Used to
# delay needed to download full Specification objects when only the +name+
diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
index 8932e068be..b80f882c77 100644
--- a/lib/rubygems/resolver/installed_specification.rb
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# An InstalledSpecification represents a gem that is already installed
# locally.
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index f663ce4ad5..521d241fd5 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set of gems for installation sourced from remote sources and local .gem
# files
@@ -66,7 +67,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
found = found.select do |s|
Gem::Source::SpecificFile === s.source ||
- Gem::Platform.match(s.platform)
+ Gem::Platform.match_spec?(s)
end
found = found.sort_by do |s|
@@ -147,6 +148,8 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
+ matching_local = []
+
if consider_local?
matching_local = @local.values.select do |spec, _|
req.match? spec
@@ -167,7 +170,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
- res.concat @remote_set.find_all req if consider_remote?
+ res.concat @remote_set.find_all req if consider_remote? && matching_local.empty?
res
end
diff --git a/lib/rubygems/resolver/local_specification.rb b/lib/rubygems/resolver/local_specification.rb
index c27bab0f5a..50bf276345 100644
--- a/lib/rubygems/resolver/local_specification.rb
+++ b/lib/rubygems/resolver/local_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A LocalSpecification comes from a .gem file on the local filesystem.
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index b1a5433cb5..8eec041bdc 100644
--- a/lib/rubygems/resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A set of gems from a gem dependencies lockfile.
diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb
index 7de2a14658..06f912dd85 100644
--- a/lib/rubygems/resolver/lock_specification.rb
+++ b/lib/rubygems/resolver/lock_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile).
#
diff --git a/lib/rubygems/resolver/molinillo.rb b/lib/rubygems/resolver/molinillo.rb
index e154342571..d703505410 100644
--- a/lib/rubygems/resolver/molinillo.rb
+++ b/lib/rubygems/resolver/molinillo.rb
@@ -1,2 +1,3 @@
# frozen_string_literal: true
+
require_relative "molinillo/lib/molinillo"
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index 95f8416b96..731a9e3e90 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -32,7 +32,7 @@ module Gem::Resolver::Molinillo
# all belong to the same graph.
# @return [Array<Vertex>] The sorted vertices.
def self.tsort(vertices)
- TSort.tsort(
+ Gem::TSort.tsort(
lambda { |b| vertices.each(&b) },
lambda { |v, &b| (v.successors & vertices).each(&b) }
)
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
index ada03a901c..4289902828 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
@@ -107,36 +107,42 @@ module Gem::Resolver::Molinillo
end
end
- conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
- o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = reduce_trees.call(conflict.requirement_trees)
-
- o << trees.map do |tree|
- t = ''.dup
- depth = 2
- tree.each do |req|
- t << ' ' * depth << printable_requirement.call(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[name_for(req)]
- t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ full_message_for_conflict = opts.delete(:full_message_for_conflict) do
+ proc do |name, conflict|
+ o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
+ if conflict.locked_requirement
+ o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
+ o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
+ o << %(\n)
+ end
+ o << %( In #{name_for_explicit_dependency_source}:\n)
+ trees = reduce_trees.call(conflict.requirement_trees)
+
+ o << trees.map do |tree|
+ t = ''.dup
+ depth = 2
+ tree.each do |req|
+ t << ' ' * depth << printable_requirement.call(req)
+ unless tree.last == req
+ if spec = conflict.activated_by_name[name_for(req)]
+ t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ end
+ t << %( depends on)
end
- t << %( depends on)
+ t << %(\n)
+ depth += 1
end
- t << %(\n)
- depth += 1
- end
- t
- end.join("\n")
+ t
+ end.join("\n")
- additional_message_for_conflict.call(o, name, conflict)
+ additional_message_for_conflict.call(o, name, conflict)
- o
+ o
+ end
+ end
+
+ conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
+ o << full_message_for_conflict.call(name, conflict)
end.strip
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
index 6b5ada7ade..86c249c404 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
@@ -2,5 +2,5 @@
module Gem::Resolver::Molinillo
# The version of Gem::Resolver::Molinillo.
- VERSION = '0.7.0'.freeze
+ VERSION = '0.8.0'.freeze
end
diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb
index 5b51493c9a..6f86f0f412 100644
--- a/lib/rubygems/resolver/requirement_list.rb
+++ b/lib/rubygems/resolver/requirement_list.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The RequirementList is used to hold the requirements being considered
# while resolving a set of gems.
diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb
index 5d8dd51eaa..243fee5fd5 100644
--- a/lib/rubygems/resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Resolver sets are used to look up specifications (and their
# dependencies) used in resolution. This set is abstract.
diff --git a/lib/rubygems/resolver/source_set.rb b/lib/rubygems/resolver/source_set.rb
index bf8c23184e..296cf41078 100644
--- a/lib/rubygems/resolver/source_set.rb
+++ b/lib/rubygems/resolver/source_set.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# The SourceSet chooses the best available method to query a remote index.
#
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index 7b665fe876..79a34d8063 100644
--- a/lib/rubygems/resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The Resolver::SpecSpecification contains common functionality for
# Resolver specifications that are backed by a Gem::Specification.
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index 3da803cab5..d2098ef0e2 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A Resolver::Specification contains a subset of the information
# contained in a Gem::Specification. Only the information necessary for
diff --git a/lib/rubygems/resolver/stats.rb b/lib/rubygems/resolver/stats.rb
index 64b458f504..9920976b2a 100644
--- a/lib/rubygems/resolver/stats.rb
+++ b/lib/rubygems/resolver/stats.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class Gem::Resolver::Stats
def initialize
@max_depth = 0
@@ -32,7 +33,7 @@ class Gem::Resolver::Stats
@iterations += 1
end
- PATTERN = "%20s: %d\n".freeze
+ PATTERN = "%20s: %d\n"
def display
$stdout.puts "=== Resolver Statistics ==="
diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
index 6c0ef2a1a1..293a1e3331 100644
--- a/lib/rubygems/resolver/vendor_set.rb
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A VendorSet represents gems that have been unpacked into a specific
# directory that contains a gemspec.
diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
index 600a98a2bf..ac78f54558 100644
--- a/lib/rubygems/resolver/vendor_specification.rb
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A VendorSpecification represents a gem that has been unpacked into a project
# and is being loaded through a gem dependencies file through the +path:+
diff --git a/lib/rubygems/s3_uri_signer.rb b/lib/rubygems/s3_uri_signer.rb
index 5522753af5..5161e43ebb 100644
--- a/lib/rubygems/s3_uri_signer.rb
+++ b/lib/rubygems/s3_uri_signer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "openssl"
##
@@ -170,6 +172,6 @@ class Gem::S3URISigner
end
BASE64_URI_TRANSLATE = { "+" => "%2B", "/" => "%2F", "=" => "%3D", "\n" => "" }.freeze
- EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info".freeze
- EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/".freeze
+ EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info"
+ EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
end
diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb
index 5a98505598..74ad25ca15 100644
--- a/lib/rubygems/safe_yaml.rb
+++ b/lib/rubygems/safe_yaml.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gem
###
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index dd16283a98..73ee1e4bb9 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -376,8 +377,8 @@ module Gem::Security
# * The certificate contains a subject key identifier
EXTENSIONS = {
- "basicConstraints" => "CA:FALSE",
- "keyUsage" =>
+ "basicConstraints" => "CA:FALSE",
+ "keyUsage" =>
"keyEncipherment,dataEncipherment,digitalSignature",
"subjectKeyIdentifier" => "hash",
}.freeze
@@ -434,13 +435,6 @@ module Gem::Security
end
##
- # In Ruby 2.3 EC doesn't implement the private_key? but not the private? method
-
- if defined?(OpenSSL::PKey::EC) && Gem::Version.new(String.new(RUBY_VERSION)) < Gem::Version.new("2.4.0")
- OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
- end
-
- ##
# Creates a self-signed certificate with an issuer and subject from +email+,
# a subject alternative name of +email+ and the given +extensions+ for the
# +key+.
@@ -492,13 +486,7 @@ module Gem::Security
when "rsa"
OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH)
when "ec"
- if RUBY_VERSION >= "2.4.0"
- OpenSSL::PKey::EC.generate(EC_NAME)
- else
- domain_key = OpenSSL::PKey::EC.new(EC_NAME)
- domain_key.generate_key
- domain_key
- end
+ OpenSSL::PKey::EC.generate(EC_NAME)
else
raise Gem::Security::Exception,
"#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported."
diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb
index b3f9070394..bdfe9ed43f 100644
--- a/lib/rubygems/security/policies.rb
+++ b/lib/rubygems/security/policies.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Security
##
@@ -6,12 +7,12 @@ module Gem::Security
NoSecurity = Policy.new(
"No Security",
- :verify_data => false,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ :verify_data => false,
+ :verify_signer => false,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -24,12 +25,12 @@ module Gem::Security
AlmostNoSecurity = Policy.new(
"Almost No Security",
- :verify_data => true,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ :verify_data => true,
+ :verify_signer => false,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -41,12 +42,12 @@ module Gem::Security
LowSecurity = Policy.new(
"Low Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => false,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
)
##
@@ -60,12 +61,12 @@ module Gem::Security
MediumSecurity = Policy.new(
"Medium Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => false
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => true,
+ :only_signed => false
)
##
@@ -79,12 +80,12 @@ module Gem::Security
HighSecurity = Policy.new(
"High Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => true
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => true,
+ :only_signed => true
)
##
@@ -92,23 +93,23 @@ module Gem::Security
SigningPolicy = Policy.new(
"Signing Policy",
- :verify_data => false,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => false,
- :only_signed => false
+ :verify_data => false,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => false,
+ :only_signed => false
)
##
# Hash of configured security policies
Policies = {
- "NoSecurity" => NoSecurity,
+ "NoSecurity" => NoSecurity,
"AlmostNoSecurity" => AlmostNoSecurity,
- "LowSecurity" => LowSecurity,
- "MediumSecurity" => MediumSecurity,
- "HighSecurity" => HighSecurity,
+ "LowSecurity" => LowSecurity,
+ "MediumSecurity" => MediumSecurity,
+ "HighSecurity" => HighSecurity,
# SigningPolicy is not intended for use by `gem -P` so do not list it
}.freeze
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 959880ddc1..9b9eac46bf 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "../user_interaction"
##
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index cca82f1cf8..d76af03eb8 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Basic OpenSSL-based package signing class.
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index a6882c66e7..4de33068f9 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The TrustDir manages the trusted certificates for gem signature
# verification.
@@ -8,7 +9,7 @@ class Gem::Security::TrustDir
# Default permissions for the trust directory and its contents
DEFAULT_PERMISSIONS = {
- :trust_dir => 0700,
+ :trust_dir => 0700,
:trusted_cert => 0600,
}.freeze
diff --git a/lib/rubygems/security_option.rb b/lib/rubygems/security_option.rb
index ab3898bf11..f288ebd047 100644
--- a/lib/rubygems/security_option.rb
+++ b/lib/rubygems/security_option.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/shellwords.rb b/lib/rubygems/shellwords.rb
new file mode 100644
index 0000000000..741dccb363
--- /dev/null
+++ b/lib/rubygems/shellwords.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+autoload :Shellwords, "shellwords"
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index fc72a1038a..aa0cbc1641 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -12,8 +12,8 @@ class Gem::Source
include Gem::Text
FILES = { # :nodoc:
- :released => "specs",
- :latest => "latest_specs",
+ :released => "specs",
+ :latest => "latest_specs",
:prerelease => "prerelease_specs",
}.freeze
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 2609a309e8..7ac685f978 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -53,7 +53,7 @@ class Gem::Source::Git < Gem::Source
@uri = Gem::Uri.parse(repository)
@name = name
@repository = repository
- @reference = reference
+ @reference = reference || "HEAD"
@need_submodules = submodules
@remote = true
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
index 786faab3e3..f5d3f06d6a 100644
--- a/lib/rubygems/source/installed.rb
+++ b/lib/rubygems/source/installed.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Represents an installed gem. This is used for dependency resolution.
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index ec1a594238..5068b0b906 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# The local source finds gems in the current directory for fulfilling
# dependencies.
diff --git a/lib/rubygems/source/lock.rb b/lib/rubygems/source/lock.rb
index 49f097467b..f9388bbd61 100644
--- a/lib/rubygems/source/lock.rb
+++ b/lib/rubygems/source/lock.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A Lock source wraps an installed gem's source and sorts before other sources
# during dependency resolution. This allows RubyGems to prefer gems from
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
index 552aeba50f..61965c2644 100644
--- a/lib/rubygems/source/specific_file.rb
+++ b/lib/rubygems/source/specific_file.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# A source representing a single .gem file. This is used for installation of
# local gems.
diff --git a/lib/rubygems/source/vendor.rb b/lib/rubygems/source/vendor.rb
index 543acf1388..12161b8cf5 100644
--- a/lib/rubygems/source/vendor.rb
+++ b/lib/rubygems/source/vendor.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# This represents a vendored source that is similar to an installed gem.
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 0d06d1f144..a5a4fa0c0f 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "remote_fetcher"
require_relative "user_interaction"
require_relative "errors"
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index af07cd36e2..7611e1ba1f 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
@@ -75,7 +76,7 @@ class Gem::Specification < Gem::BasicSpecification
SPECIFICATION_VERSION_HISTORY = { # :nodoc:
-1 => ["(RubyGems versions up to and including 0.7 did not have versioned specifications)"],
- 1 => [
+ 1 => [
'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
'"test_file=x" is a shortcut for "test_files=[x]"',
],
@@ -93,10 +94,10 @@ class Gem::Specification < Gem::BasicSpecification
MARSHAL_FIELDS = { # :nodoc:
-1 => 16,
- 1 => 16,
- 2 => 16,
- 3 => 17,
- 4 => 18,
+ 1 => 16,
+ 2 => 16,
+ 3 => 17,
+ 4 => 18,
}.freeze
today = Time.now.utc
@@ -124,35 +125,35 @@ class Gem::Specification < Gem::BasicSpecification
# Map of attribute names to default values.
@@default_value = {
- :authors => [],
- :autorequire => nil,
- :bindir => "bin",
- :cert_chain => [],
- :date => nil,
- :dependencies => [],
- :description => nil,
- :email => nil,
- :executables => [],
- :extensions => [],
- :extra_rdoc_files => [],
- :files => [],
- :homepage => nil,
- :licenses => [],
- :metadata => {},
- :name => nil,
- :platform => Gem::Platform::RUBY,
- :post_install_message => nil,
- :rdoc_options => [],
- :require_paths => ["lib"],
- :required_ruby_version => Gem::Requirement.default,
+ :authors => [],
+ :autorequire => nil,
+ :bindir => "bin",
+ :cert_chain => [],
+ :date => nil,
+ :dependencies => [],
+ :description => nil,
+ :email => nil,
+ :executables => [],
+ :extensions => [],
+ :extra_rdoc_files => [],
+ :files => [],
+ :homepage => nil,
+ :licenses => [],
+ :metadata => {},
+ :name => nil,
+ :platform => Gem::Platform::RUBY,
+ :post_install_message => nil,
+ :rdoc_options => [],
+ :require_paths => ["lib"],
+ :required_ruby_version => Gem::Requirement.default,
:required_rubygems_version => Gem::Requirement.default,
- :requirements => [],
- :rubygems_version => Gem::VERSION,
- :signing_key => nil,
- :specification_version => CURRENT_SPECIFICATION_VERSION,
- :summary => nil,
- :test_files => [],
- :version => nil,
+ :requirements => [],
+ :rubygems_version => Gem::VERSION,
+ :signing_key => nil,
+ :specification_version => CURRENT_SPECIFICATION_VERSION,
+ :summary => nil,
+ :test_files => [],
+ :version => nil,
}.freeze
# rubocop:disable Style/MutableConstant
@@ -338,7 +339,7 @@ class Gem::Specification < Gem::BasicSpecification
# The simplest way is to specify the standard SPDX ID
# https://spdx.org/licenses/ for the license.
# Ideally, you should pick one that is OSI (Open Source Initiative)
- # http://opensource.org/licenses/alphabetical approved.
+ # https://opensource.org/licenses/ approved.
#
# The most commonly used OSI-approved licenses are MIT and Apache-2.0.
# GitHub also provides a license picker at http://choosealicense.com/.
@@ -1022,6 +1023,12 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # Find the best specification matching a +full_name+.
+ def self.find_by_full_name(full_name)
+ stubs.find {|s| s.full_name == full_name }&.to_spec
+ end
+
+ ##
# Return the best specification that contains the file matching +path+.
def self.find_by_path(path)
@@ -1041,7 +1048,7 @@ class Gem::Specification < Gem::BasicSpecification
next if s.activated?
s.contains_requirable_file? path
end
- stub && stub.to_spec
+ stub&.to_spec
end
def self.find_active_stub_by_path(path)
@@ -1293,6 +1300,8 @@ class Gem::Specification < Gem::BasicSpecification
def self._load(str)
Gem.load_yaml
+ yaml_set = false
+
array = begin
Marshal.load str
rescue ArgumentError => e
@@ -1305,7 +1314,10 @@ class Gem::Specification < Gem::BasicSpecification
message = e.message
raise unless message.include?("YAML::")
- Object.const_set "YAML", Psych unless Object.const_defined?(:YAML)
+ unless Object.const_defined?(:YAML)
+ Object.const_set "YAML", Psych
+ yaml_set = true
+ end
if message.include?("YAML::Syck::")
YAML.const_set "Syck", YAML unless YAML.const_defined?(:Syck)
@@ -1316,6 +1328,8 @@ class Gem::Specification < Gem::BasicSpecification
end
retry
+ ensure
+ Object.__send__(:remove_const, "YAML") if yaml_set
end
spec = Gem::Specification.new
@@ -1606,6 +1620,8 @@ class Gem::Specification < Gem::BasicSpecification
def build_extensions # :nodoc:
return if extensions.empty?
return if default_gem?
+ # we need to fresh build when same name and version of default gems
+ return if self.class.find_by_full_name(full_name)&.default_gem?
return if File.exist? gem_build_complete_path
return if !File.writable?(base_dir)
return if !File.exist?(File.join(base_dir, "extensions"))
@@ -1626,7 +1642,7 @@ class Gem::Specification < Gem::BasicSpecification
builder.build_extensions
end
ensure
- ui.close if ui
+ ui&.close
Gem::Specification.unresolved_deps.replace unresolved_deps
end
end
@@ -2225,7 +2241,7 @@ class Gem::Specification < Gem::BasicSpecification
# The platform this gem runs on. See Gem::Platform for details.
def platform
- @new_platform ||= Gem::Platform::RUBY
+ @new_platform ||= Gem::Platform::RUBY # rubocop:disable Naming/MemoizedInstanceVariableName
end
def pretty_print(q) # :nodoc:
@@ -2704,6 +2720,8 @@ class Gem::Specification < Gem::BasicSpecification
end
@installed_by_version ||= nil
+
+ nil
end
def flatten_require_paths # :nodoc:
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
index 44b31211e5..cee7eb3fdc 100644
--- a/lib/rubygems/specification_policy.rb
+++ b/lib/rubygems/specification_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "user_interaction"
class Gem::SpecificationPolicy
@@ -173,6 +175,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
end
##
+ # Checks that the gem does not depend on itself.
# Checks that dependencies use requirements as we recommend. Warnings are
# issued when dependencies are open-ended or overly strict for semantic
# versioning.
@@ -180,6 +183,10 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
def validate_dependencies # :nodoc:
warning_messages = []
@specification.dependencies.each do |dep|
+ if dep.name == @specification.name # warn on self reference
+ warning_messages << "Self referencing dependency is unnecessary and strongly discouraged."
+ end
+
prerelease_dep = dep.requirements_list.any? do |req|
Gem::Requirement.new(req).prerelease?
end
@@ -460,6 +467,20 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
require_relative "ext"
builder = Gem::Ext::Builder.new(@specification)
+ validate_rake_extensions(builder)
+ validate_rust_extensions(builder)
+ end
+
+ def validate_rust_extensions(builder) # :nodoc:
+ rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
+ missing_cargo_lock = !@specification.files.any? {|f| f.end_with?("Cargo.lock") }
+
+ error <<-ERROR if rust_extension && missing_cargo_lock
+You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec.
+ ERROR
+ end
+
+ def validate_rake_extensions(builder) # :nodoc:
rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder }
rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" }
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index 33b4f45b0a..d64d611f48 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
##
# Gem::StubSpecification reads the stub: line from the gemspec. This prevents
# us having to eval the entire gemspec in order to find out certain
@@ -6,10 +7,10 @@
class Gem::StubSpecification < Gem::BasicSpecification
# :nodoc:
- PREFIX = "# stub: ".freeze
+ PREFIX = "# stub: "
# :nodoc:
- OPEN_MODE = "r:UTF-8:-".freeze
+ OPEN_MODE = "r:UTF-8:-"
class StubLine # :nodoc: all
attr_reader :name, :version, :platform, :require_paths, :extensions,
@@ -19,9 +20,9 @@ class Gem::StubSpecification < Gem::BasicSpecification
# These are common require paths.
REQUIRE_PATHS = { # :nodoc:
- "lib" => "lib".freeze,
- "test" => "test".freeze,
- "ext" => "ext".freeze,
+ "lib" => "lib",
+ "test" => "test",
+ "ext" => "ext",
}.freeze
# These are common require path lists. This hash is used to optimize
@@ -33,7 +34,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
}.freeze
def initialize(data, extensions)
- parts = data[PREFIX.length..-1].split(" ".freeze, 4)
+ parts = data[PREFIX.length..-1].split(" ", 4)
@name = parts[0].freeze
@version = if Gem::Version.correct?(parts[1])
Gem::Version.new(parts[1])
@@ -50,7 +51,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
end
path_list = parts.last
- @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0".freeze).map! do |x|
+ @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0").map! do |x|
REQUIRE_PATHS[x] || x
end
end
@@ -183,7 +184,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
##
# The full Gem::Specification for this gem, loaded from evalling its gemspec
- def to_spec
+ def spec
@spec ||= if @data
loaded = Gem.loaded_specs[name]
loaded if loaded && loaded.version == version
@@ -191,6 +192,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
@spec ||= Gem::Specification.load(loaded_from)
end
+ alias_method :to_spec, :spec
##
# Is this StubSpecification valid? i.e. have we found a stub line, OR does
diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb
index d6b891380e..be811525f2 100644
--- a/lib/rubygems/text.rb
+++ b/lib/rubygems/text.rb
@@ -9,7 +9,7 @@ module Gem::Text
# Remove any non-printable characters and make the text suitable for
# printing.
def clean_text(text)
- text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze)
+ text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".")
end
def truncate_text(text, description, max_length = 100_000)
diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb
index f68c5947d3..f825f14257 100644
--- a/lib/rubygems/tsort/lib/tsort.rb
+++ b/lib/rubygems/tsort/lib/tsort.rb
@@ -121,334 +121,332 @@
# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
#
-module Gem
- module TSort
- class Cyclic < StandardError
- end
-
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.tsort #=> [4, 2, 3, 1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.tsort # raises Gem::TSort::Cyclic
- #
- def tsort
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort(each_node, each_child)
- end
+module Gem::TSort
+ class Cyclic < StandardError
+ end
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
- #
- def TSort.tsort(each_node, each_child)
- Gem::TSort.tsort_each(each_node, each_child).to_a
- end
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.tsort #=> [4, 2, 3, 1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.tsort # raises Gem::TSort::Cyclic
+ #
+ def tsort
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort(each_node, each_child)
+ end
- # The iterator version of the #tsort method.
- # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #tsort_each returns +nil+.
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.tsort_each {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def tsort_each(&block) # :yields: node
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort_each(each_node, each_child, &block)
- end
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
+ #
+ def self.tsort(each_node, each_child)
+ tsort_each(each_node, each_child).to_a
+ end
- # The iterator version of the Gem::TSort.tsort method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def TSort.tsort_each(each_node, each_child) # :yields: node
- return to_enum(__method__, each_node, each_child) unless block_given?
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.tsort_each {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def tsort_each(&block) # :yields: node
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort_each(each_node, each_child, &block)
+ end
- Gem::TSort.each_strongly_connected_component(each_node, each_child) {|component|
- if component.size == 1
- yield component.first
- else
- raise Cyclic.new("topological sort failed: #{component.inspect}")
- end
- }
- end
+ # The iterator version of the Gem::TSort.tsort method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def self.tsort_each(each_node, each_child) # :yields: node
+ return to_enum(__method__, each_node, each_child) unless block_given?
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
- #
- def strongly_connected_components
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.strongly_connected_components(each_node, each_child)
- end
+ each_strongly_connected_component(each_node, each_child) {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2], [3], [1]]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2, 3], [1]]
- #
- def TSort.strongly_connected_components(each_node, each_child)
- Gem::TSort.each_strongly_connected_component(each_node, each_child).to_a
- end
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
+ #
+ def strongly_connected_components
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.strongly_connected_components(each_node, each_child)
+ end
- # The iterator version of the #strongly_connected_components method.
- # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
- # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #each_strongly_connected_component returns +nil+.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def each_strongly_connected_component(&block) # :yields: nodes
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
- end
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2], [3], [1]]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2, 3], [1]]
+ #
+ def self.strongly_connected_components(each_node, each_child)
+ each_strongly_connected_component(each_node, each_child).to_a
+ end
- # The iterator version of the Gem::TSort.strongly_connected_components method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes
- return to_enum(__method__, each_node, each_child) unless block_given?
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def each_strongly_connected_component(&block) # :yields: nodes
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
+ end
- id_map = {}
- stack = []
- each_node.call {|node|
- unless id_map.include? node
- Gem::TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
- yield c
- }
- end
- }
- nil
- end
+ # The iterator version of the Gem::TSort.strongly_connected_components method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
+ return to_enum(__method__, each_node, each_child) unless block_given?
- # Iterates over strongly connected component in the subgraph reachable from
- # _node_.
- #
- # Return value is unspecified.
- #
- # #each_strongly_connected_component_from doesn't call #tsort_each_node.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- #
- def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
- Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
- end
+ id_map = {}
+ stack = []
+ each_node.call {|node|
+ unless id_map.include? node
+ each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
- # Iterates over strongly connected components in a graph.
- # The graph is represented by _node_ and _each_child_.
- #
- # _node_ is the first node.
- # _each_child_ should have +call+ method which takes a node argument
- # and yields for each child node.
- #
- # Return value is unspecified.
- #
- # #Gem::TSort.each_strongly_connected_component_from is a class method and
- # it doesn't need a class to represent a graph which includes Gem::TSort.
- #
- # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_child = lambda {|n, &b| graph[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
- # p scc
- # }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
- return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
+ Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
+ end
- minimum_id = node_id = id_map[node] = id_map.size
- stack_length = stack.length
- stack << node
+ # Iterates over strongly connected components in a graph.
+ # The graph is represented by _node_ and _each_child_.
+ #
+ # _node_ is the first node.
+ # _each_child_ should have +call+ method which takes a node argument
+ # and yields for each child node.
+ #
+ # Return value is unspecified.
+ #
+ # #Gem::TSort.each_strongly_connected_component_from is a class method and
+ # it doesn't need a class to represent a graph which includes Gem::TSort.
+ #
+ # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_child = lambda {|n, &b| graph[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ # p scc
+ # }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
+ return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
- each_child.call(node) {|child|
- if id_map.include? child
- child_id = id_map[child]
- minimum_id = child_id if child_id && child_id < minimum_id
- else
- sub_minimum_id =
- Gem::TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
- yield c
- }
- minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
- end
- }
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
- if node_id == minimum_id
- component = stack.slice!(stack_length .. -1)
- component.each {|n| id_map[n] = nil}
- yield component
+ each_child.call(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
end
+ }
- minimum_id
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
end
- # Should be implemented by a extended class.
- #
- # #tsort_each_node is used to iterate for all nodes over a graph.
- #
- def tsort_each_node # :yields: node
- raise NotImplementedError.new
- end
+ minimum_id
+ end
- # Should be implemented by a extended class.
- #
- # #tsort_each_child is used to iterate for child nodes of _node_.
- #
- def tsort_each_child(node) # :yields: child
- raise NotImplementedError.new
- end
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
end
end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 5883ed1c41..f678f960f0 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/update_suggestion.rb b/lib/rubygems/update_suggestion.rb
new file mode 100644
index 0000000000..c2e81b2374
--- /dev/null
+++ b/lib/rubygems/update_suggestion.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+##
+# Mixin methods for Gem::Command to promote available RubyGems update
+
+module Gem::UpdateSuggestion
+ # list taken from https://github.com/watson/ci-info/blob/7a3c30d/index.js#L56-L66
+ CI_ENV_VARS = [
+ "CI", # Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari
+ "CONTINUOUS_INTEGRATION", # Travis CI, Cirrus CI
+ "BUILD_NUMBER", # Jenkins, TeamCity
+ "CI_APP_ID", "CI_BUILD_ID", "CI_BUILD_NUMBER", # Applfow
+ "RUN_ID" # TaskCluster, dsari
+ ].freeze
+
+ ONE_WEEK = 7 * 24 * 60 * 60
+
+ ##
+ # Message to promote available RubyGems update with related gem update command.
+
+ def update_suggestion
+ <<-MESSAGE
+
+A new release of RubyGems is available: #{Gem.rubygems_version} → #{Gem.latest_rubygems_version}!
+Run `gem update --system #{Gem.latest_rubygems_version}` to update your installation.
+
+ MESSAGE
+ end
+
+ ##
+ # Determines if current environment is eglible for update suggestion.
+
+ def eglible_for_update?
+ # explicit opt-out
+ return false if Gem.configuration[:prevent_update_suggestion]
+ return false if ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"]
+
+ # focus only on human usage of final RubyGems releases
+ return false unless Gem.ui.tty?
+ return false if Gem.rubygems_version.prerelease?
+ return false if Gem.disable_system_update_message
+ return false if ci?
+
+ # check makes sense only when we can store timestamp of last try
+ # otherwise we will not be able to prevent "annoying" update message
+ # on each command call
+ return unless Gem.configuration.state_file_writable?
+
+ # load time of last check, ensure the difference is enough to repeat the suggestion
+ check_time = Time.now.to_i
+ last_update_check = Gem.configuration.last_update_check
+ return false if (check_time - last_update_check) < ONE_WEEK
+
+ # compare current and latest version, this is the part where
+ # latest rubygems spec is fetched from remote
+ (Gem.rubygems_version < Gem.latest_rubygems_version).tap do |eglible|
+ # store the time of last successful check into state file
+ Gem.configuration.last_update_check = check_time
+
+ return eglible
+ end
+ rescue # don't block install command on any problem
+ false
+ end
+
+ def ci?
+ CI_ENV_VARS.any? {|var| ENV.include?(var) }
+ end
+end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 2fa505423b..451dba070f 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -239,6 +240,7 @@ class Gem::StreamUI
return nil, nil unless result
result = result.strip.to_i - 1
+ return nil, nil unless (0...list.size) === result
return list[result], result
end
@@ -290,7 +292,7 @@ class Gem::StreamUI
@outs.flush
result = @ins.gets
- result.chomp! if result
+ result&.chomp!
result
end
@@ -305,7 +307,7 @@ class Gem::StreamUI
password = _gets_noecho
@outs.puts
- password.chomp! if password
+ password&.chomp!
password
end
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 356c46a5a1..bd6ea92cc9 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative "deprecate"
##
@@ -97,11 +98,7 @@ module Gem::Util
# returning absolute paths to the matching files.
def self.glob_files_in_dir(glob, base_path)
- if RUBY_VERSION >= "2.5"
- Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
- else
- Dir.glob(File.expand_path(glob, base_path))
- end
+ Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) }
end
##
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index 96f47781c0..1d5efde576 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
+
require_relative "../text"
class Gem::Licenses
extend Gem::Text
- NONSTANDARD = "Nonstandard".freeze
- LICENSE_REF = "LicenseRef-.+".freeze
+ NONSTANDARD = "Nonstandard"
+ LICENSE_REF = "LicenseRef-.+"
# Software Package Data Exchange (SPDX) standard open-source software
# license identifiers
diff --git a/lib/rubygems/util/list.rb b/lib/rubygems/util/list.rb
index 33c40af4bb..acb6c73ddb 100644
--- a/lib/rubygems/util/list.rb
+++ b/lib/rubygems/util/list.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem
class List
include Enumerable
diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb
index 1609924607..63493638b6 100644
--- a/lib/rubygems/validator.rb
+++ b/lib/rubygems/validator.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index f67889ef1a..c319e1f820 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -155,7 +155,7 @@ require_relative "deprecate"
class Gem::Version
include Comparable
- VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc:
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc:
##
@@ -272,7 +272,7 @@ class Gem::Version
# string for backwards (RubyGems 1.3.5 and earlier) compatibility.
def marshal_dump
- [version]
+ [@version]
end
##
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index a487a0bc24..d83a69cf0d 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
diff --git a/lib/securerandom.gemspec b/lib/securerandom.gemspec
index 900713e7dc..e095244ce9 100644
--- a/lib/securerandom.gemspec
+++ b/lib/securerandom.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "securerandom"
- spec.version = "0.2.0"
+ spec.version = "0.2.2"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/set.rb b/lib/set.rb
index 0490654183..df1e68d081 100644
--- a/lib/set.rb
+++ b/lib/set.rb
@@ -66,8 +66,8 @@
#
# First, what's elsewhere. \Class \Set:
#
-# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-Object-label-What-27s+Here].
-# - Includes {module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-What-27s+Here],
+# - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
+# - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
# which provides dozens of additional methods.
#
# In particular, class \Set does not have many methods of its own
diff --git a/lib/set/set.gemspec b/lib/set/set.gemspec
index 83100df785..0def52e859 100644
--- a/lib/set/set.gemspec
+++ b/lib/set/set.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "set"
- spec.version = "1.0.2"
+ spec.version = "1.0.3"
spec.authors = ["Akinori MUSHA"]
spec.email = ["knu@idaemons.org"]
diff --git a/lib/syntax_suggest/api.rb b/lib/syntax_suggest/api.rb
index 5b725e13d7..74e53c2563 100644
--- a/lib/syntax_suggest/api.rb
+++ b/lib/syntax_suggest/api.rb
@@ -78,7 +78,7 @@ module SyntaxSuggest
code_lines: search.code_lines
).call
rescue Timeout::Error => e
- io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
+ io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with SYNTAX_SUGGEST_DEBUG=1 for more info"
io.puts e.backtrace.first(3).join($/)
end
@@ -91,7 +91,9 @@ module SyntaxSuggest
dir = Pathname(dir)
dir.join(time).tap { |path|
path.mkpath
- FileUtils.ln_sf(time, dir.join("last"))
+ alias_dir = dir.join("last")
+ FileUtils.rm_rf(alias_dir) if alias_dir.exist?
+ FileUtils.ln_sf(time, alias_dir)
}
end
diff --git a/lib/syntax_suggest/around_block_scan.rb b/lib/syntax_suggest/around_block_scan.rb
index 2a57d1b19e..ce00431b3a 100644
--- a/lib/syntax_suggest/around_block_scan.rb
+++ b/lib/syntax_suggest/around_block_scan.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require_relative "scan_history"
+
module SyntaxSuggest
# This class is useful for exploring contents before and after
# a block
@@ -24,201 +26,207 @@ module SyntaxSuggest
# puts scan.before_index # => 0
# puts scan.after_index # => 3
#
- # Contents can also be filtered using AroundBlockScan#skip
- #
- # To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
class AroundBlockScan
def initialize(code_lines:, block:)
@code_lines = code_lines
- @orig_before_index = block.lines.first.index
- @orig_after_index = block.lines.last.index
@orig_indent = block.current_indent
- @skip_array = []
- @after_array = []
- @before_array = []
- @stop_after_kw = false
- @skip_hidden = false
- @skip_empty = false
+ @stop_after_kw = false
+ @force_add_empty = false
+ @force_add_hidden = false
+ @target_indent = nil
+
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ end
+
+ # When using this flag, `scan_while` will
+ # bypass the block it's given and always add a
+ # line that responds truthy to `CodeLine#hidden?`
+ #
+ # Lines are hidden when they've been evaluated by
+ # the parser as part of a block and found to contain
+ # valid code.
+ def force_add_hidden
+ @force_add_hidden = true
+ self
end
- def skip(name)
- case name
- when :hidden?
- @skip_hidden = true
- when :empty?
- @skip_empty = true
- else
- raise "Unsupported skip #{name}"
- end
+ # When using this flag, `scan_while` will
+ # bypass the block it's given and always add a
+ # line that responds truthy to `CodeLine#empty?`
+ #
+ # Empty lines contain no code, only whitespace such
+ # as leading spaces a newline.
+ def force_add_empty
+ @force_add_empty = true
self
end
+ # Tells `scan_while` to look for mismatched keyword/end-s
+ #
+ # When scanning up, if we see more keywords then end-s it will
+ # stop. This might happen when scanning outside of a method body.
+ # the first scan line up would be a keyword and this setting would
+ # trigger a stop.
+ #
+ # When scanning down, stop if there are more end-s than keywords.
def stop_after_kw
@stop_after_kw = true
self
end
+ # Main work method
+ #
+ # The scan_while method takes a block that yields lines above and
+ # below the block. If the yield returns true, the @before_index
+ # or @after_index are modified to include the matched line.
+ #
+ # In addition to yielding individual lines, the internals of this
+ # object give a mini DSL to handle common situations such as
+ # stopping if we've found a keyword/end mis-match in one direction
+ # or the other.
def scan_while
- stop_next = false
-
- kw_count = 0
- end_count = 0
- index = before_lines.reverse_each.take_while do |line|
- next false if stop_next
- next true if @skip_hidden && line.hidden?
- next true if @skip_empty && line.empty?
+ stop_next_up = false
+ stop_next_down = false
- kw_count += 1 if line.is_kw?
- end_count += 1 if line.is_end?
- if @stop_after_kw && kw_count > end_count
- stop_next = true
- end
-
- yield line
- end.last&.index
+ @scanner.scan(
+ up: ->(line, kw_count, end_count) {
+ next false if stop_next_up
+ next true if @force_add_hidden && line.hidden?
+ next true if @force_add_empty && line.empty?
- if index && index < before_index
- @before_index = index
- end
+ if @stop_after_kw && kw_count > end_count
+ stop_next_up = true
+ end
- stop_next = false
- kw_count = 0
- end_count = 0
- index = after_lines.take_while do |line|
- next false if stop_next
- next true if @skip_hidden && line.hidden?
- next true if @skip_empty && line.empty?
+ yield line
+ },
+ down: ->(line, kw_count, end_count) {
+ next false if stop_next_down
+ next true if @force_add_hidden && line.hidden?
+ next true if @force_add_empty && line.empty?
- kw_count += 1 if line.is_kw?
- end_count += 1 if line.is_end?
- if @stop_after_kw && end_count > kw_count
- stop_next = true
- end
+ if @stop_after_kw && end_count > kw_count
+ stop_next_down = true
+ end
- yield line
- end.last&.index
+ yield line
+ }
+ )
- if index && index > after_index
- @after_index = index
- end
self
end
- def capture_neighbor_context
- lines = []
+ # Scanning is intentionally conservative because
+ # we have no way of rolling back an agressive block (at this time)
+ #
+ # If a block was stopped for some trivial reason, (like an empty line)
+ # but the next line would have caused it to be balanced then we
+ # can check that condition and grab just one more line either up or
+ # down.
+ #
+ # For example, below if we're scanning up, line 2 might cause
+ # the scanning to stop. This is because empty lines might
+ # denote logical breaks where the user intended to chunk code
+ # which is a good place to stop and check validity. Unfortunately
+ # it also means we might have a "dangling" keyword or end.
+ #
+ # 1 def bark
+ # 2
+ # 3 end
+ #
+ # If lines 2 and 3 are in the block, then when this method is
+ # run it would see it is unbalanced, but that acquiring line 1
+ # would make it balanced, so that's what it does.
+ def lookahead_balance_one_line
kw_count = 0
end_count = 0
- before_lines.reverse_each do |line|
- next if line.empty?
- break if line.indent < @orig_indent
- next if line.indent != @orig_indent
-
+ lines.each do |line|
kw_count += 1 if line.is_kw?
end_count += 1 if line.is_end?
- if kw_count != 0 && kw_count == end_count
- lines << line
- break
- end
-
- lines << line
end
- lines.reverse!
-
- kw_count = 0
- end_count = 0
- after_lines.each do |line|
- next if line.empty?
- break if line.indent < @orig_indent
- next if line.indent != @orig_indent
-
- kw_count += 1 if line.is_kw?
- end_count += 1 if line.is_end?
- if kw_count != 0 && kw_count == end_count
- lines << line
- break
+ return self if kw_count == end_count # nothing to balance
+
+ @scanner.commit_if_changed # Rollback point if we don't find anything to optimize
+
+ # Try to eat up empty lines
+ @scanner.scan(
+ up: ->(line, _, _) { line.hidden? || line.empty? },
+ down: ->(line, _, _) { line.hidden? || line.empty? }
+ )
+
+ # More ends than keywords, check if we can balance expanding up
+ next_up = @scanner.next_up
+ next_down = @scanner.next_down
+ case end_count - kw_count
+ when 1
+ if next_up&.is_kw? && next_up.indent >= @target_indent
+ @scanner.scan(
+ up: ->(line, _, _) { line == next_up },
+ down: ->(line, _, _) { false }
+ )
+ @scanner.commit_if_changed
end
-
- lines << line
- end
-
- lines
- end
-
- def on_falling_indent
- last_indent = @orig_indent
- before_lines.reverse_each do |line|
- next if line.empty?
- if line.indent < last_indent
- yield line
- last_indent = line.indent
- end
- end
-
- last_indent = @orig_indent
- after_lines.each do |line|
- next if line.empty?
- if line.indent < last_indent
- yield line
- last_indent = line.indent
+ when -1
+ if next_down&.is_end? && next_down.indent >= @target_indent
+ @scanner.scan(
+ up: ->(line, _, _) { false },
+ down: ->(line, _, _) { line == next_down }
+ )
+ @scanner.commit_if_changed
end
end
- end
-
- def scan_neighbors
- scan_while { |line| line.not_empty? && line.indent >= @orig_indent }
- end
+ # Rollback any uncommitted changes
+ @scanner.stash_changes
- def next_up
- @code_lines[before_index.pred]
+ self
end
- def next_down
- @code_lines[after_index.next]
+ # Finds code lines at the same or greater indentation and adds them
+ # to the block
+ def scan_neighbors_not_empty
+ @target_indent = @orig_indent
+ scan_while { |line| line.not_empty? && line.indent >= @target_indent }
end
+ # Scan blocks based on indentation of next line above/below block
+ #
+ # Determines indentaion of the next line above/below the current block.
+ #
+ # Normally this is called when a block has expanded to capture all "neighbors"
+ # at the same (or greater) indentation and needs to expand out. For example
+ # the `def/end` lines surrounding a method.
def scan_adjacent_indent
before_after_indent = []
- before_after_indent << (next_up&.indent || 0)
- before_after_indent << (next_down&.indent || 0)
- indent = before_after_indent.min
- scan_while { |line| line.not_empty? && line.indent >= indent }
+ before_after_indent << (@scanner.next_up&.indent || 0)
+ before_after_indent << (@scanner.next_down&.indent || 0)
- self
- end
+ @target_indent = before_after_indent.min
+ scan_while { |line| line.not_empty? && line.indent >= @target_indent }
- def start_at_next_line
- before_index
- after_index
- @before_index -= 1
- @after_index += 1
self
end
+ # Return the currently matched lines as a `CodeBlock`
+ #
+ # When a `CodeBlock` is created it will gather metadata about
+ # itself, so this is not a free conversion. Avoid allocating
+ # more CodeBlock's than needed
def code_block
CodeBlock.new(lines: lines)
end
+ # Returns the lines matched by the current scan as an
+ # array of CodeLines
def lines
- @code_lines[before_index..after_index]
- end
-
- def before_index
- @before_index ||= @orig_before_index
- end
-
- def after_index
- @after_index ||= @orig_after_index
- end
-
- private def before_lines
- @code_lines[0...before_index] || []
+ @scanner.lines
end
- private def after_lines
- @code_lines[after_index.next..-1] || []
+ # Managable rspec errors
+ def inspect
+ "#<#{self.class}:0x0000123843lol >"
end
end
end
diff --git a/lib/syntax_suggest/block_expand.rb b/lib/syntax_suggest/block_expand.rb
index 396b2c3a1a..e9b486c720 100644
--- a/lib/syntax_suggest/block_expand.rb
+++ b/lib/syntax_suggest/block_expand.rb
@@ -35,30 +35,121 @@ module SyntaxSuggest
@code_lines = code_lines
end
+ # Main interface. Expand current indentation, before
+ # expanding to a lower indentation
def call(block)
if (next_block = expand_neighbors(block))
- return next_block
+ next_block
+ else
+ expand_indent(block)
end
-
- expand_indent(block)
end
+ # Expands code to the next lowest indentation
+ #
+ # For example:
+ #
+ # 1 def dog
+ # 2 print "dog"
+ # 3 end
+ #
+ # If a block starts on line 2 then it has captured all it's "neighbors" (code at
+ # the same indentation or higher). To continue expanding, this block must capture
+ # lines one and three which are at a different indentation level.
+ #
+ # This method allows fully expanded blocks to decrease their indentation level (so
+ # they can expand to capture more code up and down). It does this conservatively
+ # as there's no undo (currently).
def expand_indent(block)
- AroundBlockScan.new(code_lines: @code_lines, block: block)
- .skip(:hidden?)
+ now = AroundBlockScan.new(code_lines: @code_lines, block: block)
+ .force_add_hidden
.stop_after_kw
.scan_adjacent_indent
- .code_block
+
+ now.lookahead_balance_one_line
+
+ now.code_block
end
+ # A neighbor is code that is at or above the current indent line.
+ #
+ # First we build a block with all neighbors. If we can't go further
+ # then we decrease the indentation threshold and expand via indentation
+ # i.e. `expand_indent`
+ #
+ # Handles two general cases.
+ #
+ # ## Case #1: Check code inside of methods/classes/etc.
+ #
+ # It's important to note, that not everything in a given indentation level can be parsed
+ # as valid code even if it's part of valid code. For example:
+ #
+ # 1 hash = {
+ # 2 name: "richard",
+ # 3 dog: "cinco",
+ # 4 }
+ #
+ # In this case lines 2 and 3 will be neighbors, but they're invalid until `expand_indent`
+ # is called on them.
+ #
+ # When we are adding code within a method or class (at the same indentation level),
+ # use the empty lines to denote the programmer intended logical chunks.
+ # Stop and check each one. For example:
+ #
+ # 1 def dog
+ # 2 print "dog"
+ # 3
+ # 4 hash = {
+ # 5 end
+ #
+ # If we did not stop parsing at empty newlines then the block might mistakenly grab all
+ # the contents (lines 2, 3, and 4) and report them as being problems, instead of only
+ # line 4.
+ #
+ # ## Case #2: Expand/grab other logical blocks
+ #
+ # Once the search algorithm has converted all lines into blocks at a given indentation
+ # it will then `expand_indent`. Once the blocks that generates are expanded as neighbors
+ # we then begin seeing neighbors being other logical blocks i.e. a block's neighbors
+ # may be another method or class (something with keywords/ends).
+ #
+ # For example:
+ #
+ # 1 def bark
+ # 2
+ # 3 end
+ # 4
+ # 5 def sit
+ # 6 end
+ #
+ # In this case if lines 4, 5, and 6 are in a block when it tries to expand neighbors
+ # it will expand up. If it stops after line 2 or 3 it may cause problems since there's a
+ # valid kw/end pair, but the block will be checked without it.
+ #
+ # We try to resolve this edge case with `lookahead_balance_one_line` below.
def expand_neighbors(block)
- expanded_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
- .skip(:hidden?)
+ now = AroundBlockScan.new(code_lines: @code_lines, block: block)
+
+ # Initial scan
+ now
+ .force_add_hidden
.stop_after_kw
- .scan_neighbors
- .scan_while { |line| line.empty? } # Slurp up empties
+ .scan_neighbors_not_empty
+
+ # Slurp up empties
+ now
+ .scan_while { |line| line.empty? }
+
+ # If next line is kw and it will balance us, take it
+ expanded_lines = now
+ .lookahead_balance_one_line
.lines
+ # Don't allocate a block if it won't be used
+ #
+ # If nothing was taken, return nil to indicate that status
+ # used in `def call` to determine if
+ # we need to expand up/out (`expand_indent`)
if block.lines == expanded_lines
nil
else
diff --git a/lib/syntax_suggest/capture/before_after_keyword_ends.rb b/lib/syntax_suggest/capture/before_after_keyword_ends.rb
new file mode 100644
index 0000000000..f53c57a4d1
--- /dev/null
+++ b/lib/syntax_suggest/capture/before_after_keyword_ends.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ module Capture
+ # Shows surrounding kw/end pairs
+ #
+ # The purpose of showing these extra pairs is due to cases
+ # of ambiguity when only one visible line is matched.
+ #
+ # For example:
+ #
+ # 1 class Dog
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ # 6 end
+ #
+ # In this case either line 2 could be missing an `end` or
+ # line 4 was an extra line added by mistake (it happens).
+ #
+ # When we detect the above problem it shows the issue
+ # as only being on line 2
+ #
+ # 2 def bark
+ #
+ # Showing "neighbor" keyword pairs gives extra context:
+ #
+ # 2 def bark
+ # 4 def eat
+ # 5 end
+ #
+ #
+ # Example:
+ #
+ # lines = BeforeAfterKeywordEnds.new(
+ # block: block,
+ # code_lines: code_lines
+ # ).call()
+ #
+ class BeforeAfterKeywordEnds
+ def initialize(code_lines:, block:)
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ @original_indent = block.current_indent
+ end
+
+ def call
+ lines = []
+
+ @scanner.scan(
+ up: ->(line, kw_count, end_count) {
+ next true if line.empty?
+ break if line.indent < @original_indent
+ next true if line.indent != @original_indent
+
+ # If we're going up and have one complete kw/end pair, stop
+ if kw_count != 0 && kw_count == end_count
+ lines << line
+ break
+ end
+
+ lines << line if line.is_kw? || line.is_end?
+ true
+ },
+ down: ->(line, kw_count, end_count) {
+ next true if line.empty?
+ break if line.indent < @original_indent
+ next true if line.indent != @original_indent
+
+ # if we're going down and have one complete kw/end pair,stop
+ if kw_count != 0 && kw_count == end_count
+ lines << line
+ break
+ end
+
+ lines << line if line.is_kw? || line.is_end?
+ true
+ }
+ )
+ @scanner.stash_changes
+
+ lines
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/capture/falling_indent_lines.rb b/lib/syntax_suggest/capture/falling_indent_lines.rb
new file mode 100644
index 0000000000..1e046b2ba5
--- /dev/null
+++ b/lib/syntax_suggest/capture/falling_indent_lines.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ module Capture
+ # Shows the context around code provided by "falling" indentation
+ #
+ # If this is the original code lines:
+ #
+ # class OH
+ # def hello
+ # it "foo" do
+ # end
+ # end
+ #
+ # And this is the line that is captured
+ #
+ # it "foo" do
+ #
+ # It will yield its surrounding context:
+ #
+ # class OH
+ # def hello
+ # end
+ # end
+ #
+ # Example:
+ #
+ # FallingIndentLines.new(
+ # block: block,
+ # code_lines: @code_lines
+ # ).call do |line|
+ # @lines_to_output << line
+ # end
+ #
+ class FallingIndentLines
+ def initialize(code_lines:, block:)
+ @lines = nil
+ @scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ @original_indent = block.current_indent
+ end
+
+ def call(&yieldable)
+ last_indent_up = @original_indent
+ last_indent_down = @original_indent
+
+ @scanner.commit_if_changed
+ @scanner.scan(
+ up: ->(line, _, _) {
+ next true if line.empty?
+
+ if line.indent < last_indent_up
+ yieldable.call(line)
+ last_indent_up = line.indent
+ end
+ true
+ },
+ down: ->(line, _, _) {
+ next true if line.empty?
+
+ if line.indent < last_indent_down
+ yieldable.call(line)
+ last_indent_down = line.indent
+ end
+ true
+ }
+ )
+ @scanner.stash_changes
+ end
+ end
+ end
+end
diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb
index c74a366a25..6dc7047176 100644
--- a/lib/syntax_suggest/capture_code_context.rb
+++ b/lib/syntax_suggest/capture_code_context.rb
@@ -1,6 +1,14 @@
# frozen_string_literal: true
module SyntaxSuggest
+ module Capture
+ end
+end
+
+require_relative "capture/falling_indent_lines"
+require_relative "capture/before_after_keyword_ends"
+
+module SyntaxSuggest
# Turns a "invalid block(s)" into useful context
#
# There are three main phases in the algorithm:
@@ -55,6 +63,10 @@ module SyntaxSuggest
capture_falling_indent(block)
end
+ sorted_lines
+ end
+
+ def sorted_lines
@lines_to_output.select!(&:not_empty?)
@lines_to_output.uniq!
@lines_to_output.sort!
@@ -76,12 +88,11 @@ module SyntaxSuggest
# end
# end
#
- #
def capture_falling_indent(block)
- AroundBlockScan.new(
+ Capture::FallingIndentLines.new(
block: block,
code_lines: @code_lines
- ).on_falling_indent do |line|
+ ).call do |line|
@lines_to_output << line
end
end
@@ -116,9 +127,10 @@ module SyntaxSuggest
def capture_before_after_kws(block)
return unless block.visible_lines.count == 1
- around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
- .start_at_next_line
- .capture_neighbor_context
+ around_lines = Capture::BeforeAfterKeywordEnds.new(
+ code_lines: @code_lines,
+ block: block
+ ).call
around_lines -= block.lines
@@ -137,10 +149,10 @@ module SyntaxSuggest
# puts "woof" # 3
# end # 4
#
- # However due to https://github.com/zombocom/syntax_suggest/issues/32
+ # However due to https://github.com/ruby/syntax_suggest/issues/32
# the problem line will be identified as:
#
- # ❯ class Dog # 1
+ # > class Dog # 1
#
# Because lines 2, 3, and 4 are technically valid code and are expanded
# first, deemed valid, and hidden. We need to un-hide the matching end
@@ -200,7 +212,7 @@ module SyntaxSuggest
#
# the problem line will be identified as:
#
- # ❯ end # 4
+ # > end # 4
#
# This happens because lines 1, 2, and 3 are technically valid code and are expanded
# first, deemed valid, and hidden. We need to un-hide the matching keyword on
diff --git a/lib/syntax_suggest/clean_document.rb b/lib/syntax_suggest/clean_document.rb
index b572189259..2c26061bfc 100644
--- a/lib/syntax_suggest/clean_document.rb
+++ b/lib/syntax_suggest/clean_document.rb
@@ -110,7 +110,7 @@ module SyntaxSuggest
@document.join
end
- # Remove comments and whitespace only lines
+ # Remove comments
#
# replace with empty newlines
#
@@ -155,8 +155,10 @@ module SyntaxSuggest
# ).to eq(2)
#
def clean_sweep(source:)
+ # Match comments, but not HEREDOC strings with #{variable} interpolation
+ # https://rubular.com/r/HPwtW9OYxKUHXQ
source.lines.map do |line|
- if line.match?(/^\s*(#[^{].*)?$/) # https://rubular.com/r/LLE10D8HKMkJvs
+ if line.match?(/^\s*#([^{].*|)$/)
$/
else
line
diff --git a/lib/syntax_suggest/cli.rb b/lib/syntax_suggest/cli.rb
index b89fa5d013..967f77bf70 100644
--- a/lib/syntax_suggest/cli.rb
+++ b/lib/syntax_suggest/cli.rb
@@ -65,6 +65,7 @@ module SyntaxSuggest
)
if display.document_ok?
+ @io.puts "Syntax OK"
@exit_obj.exit(0)
else
@exit_obj.exit(1)
@@ -91,8 +92,8 @@ module SyntaxSuggest
# ...
- ❯ 10 defdog
- ❯ 15 end
+ > 10 defdog
+ > 15 end
ENV options:
diff --git a/lib/syntax_suggest/code_line.rb b/lib/syntax_suggest/code_line.rb
index dc738ab128..a20f34afa4 100644
--- a/lib/syntax_suggest/code_line.rb
+++ b/lib/syntax_suggest/code_line.rb
@@ -48,12 +48,10 @@ module SyntaxSuggest
strip_line = line.dup
strip_line.lstrip!
- if strip_line.empty?
- @empty = true
- @indent = 0
+ @indent = if (@empty = strip_line.empty?)
+ line.length - 1 # Newline removed from strip_line is not "whitespace"
else
- @empty = false
- @indent = line.length - strip_line.length
+ line.length - strip_line.length
end
set_kw_end
diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb
index 40f5fe1375..e0fd62b81c 100644
--- a/lib/syntax_suggest/core_ext.rb
+++ b/lib/syntax_suggest/core_ext.rb
@@ -3,6 +3,10 @@
# Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require`
if SyntaxError.method_defined?(:detailed_message)
module SyntaxSuggest
+ # Mini String IO [Private]
+ #
+ # Acts like a StringIO with reduced API, but without having to require that
+ # class.
class MiniStringIO
def initialize(isatty: $stderr.isatty)
@string = +""
@@ -16,52 +20,61 @@ if SyntaxError.method_defined?(:detailed_message)
attr_reader :string
end
- end
-
- SyntaxError.prepend Module.new {
- def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
- return super unless syntax_suggest
-
- require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
-
- message = super
- file = if highlight
- SyntaxSuggest::PathnameFromMessage.new(super(highlight: false, **kwargs)).call.name
- else
- SyntaxSuggest::PathnameFromMessage.new(message).call.name
- end
-
- io = SyntaxSuggest::MiniStringIO.new
- if file
- SyntaxSuggest.call(
- io: io,
- source: file.read,
- filename: file,
- terminal: highlight
- )
- annotation = io.string
-
- annotation + message
- else
- message
- end
- rescue => e
- if ENV["SYNTAX_SUGGEST_DEBUG"]
- $stderr.warn(e.message)
- $stderr.warn(e.backtrace)
- end
-
- # Ignore internal errors
- message
+ # SyntaxSuggest.record_dir [Private]
+ #
+ # Used to monkeypatch SyntaxError via Module.prepend
+ def self.module_for_detailed_message
+ Module.new {
+ def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
+ return super unless syntax_suggest
+
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
+
+ message = super
+
+ if path
+ file = Pathname.new(path)
+ io = SyntaxSuggest::MiniStringIO.new
+
+ SyntaxSuggest.call(
+ io: io,
+ source: file.read,
+ filename: file,
+ terminal: highlight
+ )
+ annotation = io.string
+
+ annotation += "\n" unless annotation.end_with?("\n")
+
+ annotation + message
+ else
+ message
+ end
+ rescue => e
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
+ $stderr.warn(e.message)
+ $stderr.warn(e.backtrace)
+ end
+
+ # Ignore internal errors
+ message
+ end
+ }
end
- }
+ end
+
+ SyntaxError.prepend(SyntaxSuggest.module_for_detailed_message)
else
autoload :Pathname, "pathname"
+ #--
# Monkey patch kernel to ensure that all `require` calls call the same
# method
+ #++
module Kernel
+ # :stopdoc:
+
module_function
alias_method :syntax_suggest_original_require, :require
diff --git a/lib/syntax_suggest/display_code_with_line_numbers.rb b/lib/syntax_suggest/display_code_with_line_numbers.rb
index 23f4b2d1ee..a18d62e54b 100644
--- a/lib/syntax_suggest/display_code_with_line_numbers.rb
+++ b/lib/syntax_suggest/display_code_with_line_numbers.rb
@@ -14,8 +14,8 @@ module SyntaxSuggest
# # =>
# 1
# 2 def cat
- # ❯ 3 Dir.chdir
- # ❯ 4 end
+ # > 3 Dir.chdir
+ # > 4 end
# 5 end
# 6
class DisplayCodeWithLineNumbers
@@ -50,7 +50,7 @@ module SyntaxSuggest
private def format(contents:, number:, empty:, highlight: false)
string = +""
string << if highlight
- "❯ "
+ "> "
else
" "
end
diff --git a/lib/syntax_suggest/display_invalid_blocks.rb b/lib/syntax_suggest/display_invalid_blocks.rb
index bc1143f4b0..32ec0021a3 100644
--- a/lib/syntax_suggest/display_invalid_blocks.rb
+++ b/lib/syntax_suggest/display_invalid_blocks.rb
@@ -23,7 +23,6 @@ module SyntaxSuggest
def call
if document_ok?
- @io.puts "Syntax OK"
return self
end
diff --git a/lib/syntax_suggest/parse_blocks_from_indent_line.rb b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
index d1071732fe..241ed6acb4 100644
--- a/lib/syntax_suggest/parse_blocks_from_indent_line.rb
+++ b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
@@ -36,8 +36,8 @@ module SyntaxSuggest
# Builds blocks from bottom up
def each_neighbor_block(target_line)
scan = AroundBlockScan.new(code_lines: code_lines, block: CodeBlock.new(lines: target_line))
- .skip(:empty?)
- .skip(:hidden?)
+ .force_add_empty
+ .force_add_hidden
.scan_while { |line| line.indent >= target_line.indent }
neighbors = scan.code_block.lines
diff --git a/lib/syntax_suggest/pathname_from_message.rb b/lib/syntax_suggest/pathname_from_message.rb
index ea1a90856e..b6fe1617be 100644
--- a/lib/syntax_suggest/pathname_from_message.rb
+++ b/lib/syntax_suggest/pathname_from_message.rb
@@ -4,7 +4,7 @@ module SyntaxSuggest
# Converts a SyntaxError message to a path
#
# Handles the case where the filename has a colon in it
- # such as on a windows file system: https://github.com/zombocom/syntax_suggest/issues/111
+ # such as on a windows file system: https://github.com/ruby/syntax_suggest/issues/111
#
# Example:
#
diff --git a/lib/syntax_suggest/scan_history.rb b/lib/syntax_suggest/scan_history.rb
new file mode 100644
index 0000000000..d15597c440
--- /dev/null
+++ b/lib/syntax_suggest/scan_history.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+module SyntaxSuggest
+ # Scans up/down from the given block
+ #
+ # You can try out a change, stash it, or commit it to save for later
+ #
+ # Example:
+ #
+ # scanner = ScanHistory.new(code_lines: code_lines, block: block)
+ # scanner.scan(
+ # up: ->(_, _, _) { true },
+ # down: ->(_, _, _) { true }
+ # )
+ # scanner.changed? # => true
+ # expect(scanner.lines).to eq(code_lines)
+ #
+ # scanner.stash_changes
+ #
+ # expect(scanner.lines).to_not eq(code_lines)
+ class ScanHistory
+ attr_reader :before_index, :after_index
+
+ def initialize(code_lines:, block:)
+ @code_lines = code_lines
+ @history = [block]
+ refresh_index
+ end
+
+ def commit_if_changed
+ if changed?
+ @history << CodeBlock.new(lines: @code_lines[before_index..after_index])
+ end
+
+ self
+ end
+
+ # Discards any changes that have not been committed
+ def stash_changes
+ refresh_index
+ self
+ end
+
+ # Discard changes that have not been committed and revert the last commit
+ #
+ # Cannot revert the first commit
+ def revert_last_commit
+ if @history.length > 1
+ @history.pop
+ refresh_index
+ end
+
+ self
+ end
+
+ def changed?
+ @before_index != current.lines.first.index ||
+ @after_index != current.lines.last.index
+ end
+
+ # Iterates up and down
+ #
+ # Returns line, kw_count, end_count for each iteration
+ def scan(up:, down:)
+ kw_count = 0
+ end_count = 0
+
+ up_index = before_lines.reverse_each.take_while do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+ up.call(line, kw_count, end_count)
+ end.last&.index
+
+ kw_count = 0
+ end_count = 0
+
+ down_index = after_lines.each.take_while do |line|
+ kw_count += 1 if line.is_kw?
+ end_count += 1 if line.is_end?
+ down.call(line, kw_count, end_count)
+ end.last&.index
+
+ @before_index = if up_index && up_index < @before_index
+ up_index
+ else
+ @before_index
+ end
+
+ @after_index = if down_index && down_index > @after_index
+ down_index
+ else
+ @after_index
+ end
+
+ self
+ end
+
+ def next_up
+ return nil if @before_index <= 0
+
+ @code_lines[@before_index - 1]
+ end
+
+ def next_down
+ return nil if @after_index >= @code_lines.length
+
+ @code_lines[@after_index + 1]
+ end
+
+ def lines
+ @code_lines[@before_index..@after_index]
+ end
+
+ private def before_lines
+ @code_lines[0...@before_index] || []
+ end
+
+ # Returns an array of all the CodeLines that exist after
+ # the currently scanned block
+ private def after_lines
+ @code_lines[@after_index.next..-1] || []
+ end
+
+ private def current
+ @history.last
+ end
+
+ private def refresh_index
+ @before_index = current.lines.first.index
+ @after_index = current.lines.last.index
+ self
+ end
+ end
+end
diff --git a/lib/syntax_suggest/syntax_suggest.gemspec b/lib/syntax_suggest/syntax_suggest.gemspec
index acf9be7710..0e611c13d0 100644
--- a/lib/syntax_suggest/syntax_suggest.gemspec
+++ b/lib/syntax_suggest/syntax_suggest.gemspec
@@ -14,12 +14,12 @@ Gem::Specification.new do |spec|
spec.summary = "Find syntax errors in your source in a snap"
spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it'
- spec.homepage = "https://github.com/zombocom/syntax_suggest.git"
+ spec.homepage = "https://github.com/ruby/syntax_suggest.git"
spec.license = "MIT"
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = "https://github.com/zombocom/syntax_suggest.git"
+ spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git"
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|assets)/}) }
end
spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.executables = ["syntax_suggest"]
spec.require_paths = ["lib"]
end
diff --git a/lib/syntax_suggest/version.rb b/lib/syntax_suggest/version.rb
index a5176dcf2e..ac8c2f62e5 100644
--- a/lib/syntax_suggest/version.rb
+++ b/lib/syntax_suggest/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module SyntaxSuggest
- VERSION = "0.0.1"
+ VERSION = "1.1.0"
end
diff --git a/lib/tempfile.gemspec b/lib/tempfile.gemspec
index 1fa4e05196..acb26b68db 100644
--- a/lib/tempfile.gemspec
+++ b/lib/tempfile.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tempfile"
- spec.version = "0.1.2"
+ spec.version = "0.1.3"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index 8a6bcc3e14..c3263ed3c6 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -104,7 +104,7 @@ class Tempfile < DelegateClass(File)
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
- # see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
+ # see {File Permissions}[rdoc-ref:File@File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# The underlying file is removed when the \Tempfile object dies
@@ -136,12 +136,12 @@ class Tempfile < DelegateClass(File)
# Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>
#
# Keyword arguments +mode+ and +options+ are passed directly to method
- # {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
+ # {File.open}[rdoc-ref:File.open]:
#
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
- # {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
- # - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
+ # {File::Constants}[rdoc-ref:File::Constants].
+ # - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options].
#
# Related: Tempfile.create.
#
@@ -344,11 +344,11 @@ end
#
# With no block given and no arguments, creates and returns file whose:
#
-# - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile).
+# - Class is {File}[rdoc-ref:File] (not \Tempfile).
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
-# see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
+# see {File Permissions}[rdoc-ref:File@File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# With no block, the file is not removed automatically,
@@ -380,12 +380,12 @@ end
# Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
#
# Keyword arguments +mode+ and +options+ are passed directly to method
-# {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
+# {File.open}[rdoc-ref:File.open]:
#
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
-# {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
-# - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
+# {File::Constants}[rdoc-ref:File::Constants].
+# - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options].
#
# With a block given, creates the file as above, passes it to the block,
# and returns the block's value;
diff --git a/lib/time.gemspec b/lib/time.gemspec
index 72fba34204..bada91a30b 100644
--- a/lib/time.gemspec
+++ b/lib/time.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "time"
- spec.version = "0.2.0"
+ spec.version = "0.2.2"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/time.rb b/lib/time.rb
index 43c4d802e5..6a13212a49 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -509,8 +509,8 @@ class Time
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+
(\d{2,})\s+
(\d{2})\s*
- :\s*(\d{2})\s*
- (?::\s*(\d{2}))?\s+
+ :\s*(\d{2})
+ (?:\s*:\s*(\d\d))?\s+
([+-]\d{4}|
UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date
# Since RFC 2822 permit comments, the regexp has no right anchor.
diff --git a/lib/timeout.rb b/lib/timeout.rb
index 2aad1d7465..7f40bafa4d 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -23,7 +23,7 @@
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
module Timeout
- VERSION = "0.3.0"
+ VERSION = "0.3.1"
# Raised by Timeout.timeout when the block times out.
class Error < RuntimeError
@@ -120,6 +120,7 @@ module Timeout
requests.reject!(&:done?)
end
end
+ ThreadGroup::Default.add(watcher)
watcher.name = "Timeout stdlib thread"
watcher.thread_variable_set(:"\0__detached_thread__", true)
watcher
diff --git a/lib/tmpdir.gemspec b/lib/tmpdir.gemspec
index 7b76403002..e42662ea3b 100644
--- a/lib/tmpdir.gemspec
+++ b/lib/tmpdir.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tmpdir"
- spec.version = "0.1.2"
+ spec.version = "0.1.3"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
spec.description = %q{Extends the Dir class to manage the OS temporary file path.}
spec.homepage = "https://github.com/ruby/tmpdir"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 3b67164039..55920a4a74 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -19,9 +19,10 @@ class Dir
# Returns the operating system's temporary file path.
def self.tmpdir
- tmp = nil
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].each do |name, dir = ENV[name]|
- next if !dir
+ ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].find do |name, dir|
+ unless dir
+ next if !(dir = ENV[name]) or dir.empty?
+ end
dir = File.expand_path(dir)
stat = File.stat(dir) rescue next
case
@@ -32,12 +33,9 @@ class Dir
when stat.world_writable? && !stat.sticky?
warn "#{name} is world-writable: #{dir}"
else
- tmp = dir
- break
+ break dir
end
- end
- raise ArgumentError, "could not find a temporary directory" unless tmp
- tmp
+ end or raise ArgumentError, "could not find a temporary directory"
end
# Dir.mktmpdir creates a temporary directory.
@@ -108,6 +106,7 @@ class Dir
end
end
+ # Temporary name generator
module Tmpname # :nodoc:
module_function
@@ -115,16 +114,23 @@ class Dir
Dir.tmpdir
end
+ # Unusable characters as path name
UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
- class << (RANDOM = Random.new)
+ # Dedicated random number generator
+ RANDOM = Random.new
+ class << RANDOM # :nodoc:
+ # Maximum random number
MAX = 36**6 # < 0x100000000
+
+ # Returns new random string upto 6 bytes
def next
rand(MAX).to_s(36)
end
end
private_constant :RANDOM
+ # Generates and yields random names to create a temporary name
def create(basename, tmpdir=nil, max_try: nil, **opts)
origdir = tmpdir
tmpdir ||= tmpdir()
diff --git a/lib/tsort.gemspec b/lib/tsort.gemspec
index 4656d0b845..a82393b70b 100644
--- a/lib/tsort.gemspec
+++ b/lib/tsort.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tsort"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/un.gemspec b/lib/un.gemspec
index de8e6ec312..1a466fdebe 100644
--- a/lib/un.gemspec
+++ b/lib/un.gemspec
@@ -2,7 +2,7 @@
Gem::Specification.new do |spec|
spec.name = "un"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["WATANABE Hirofumi"]
spec.email = ["eban@ruby-lang.org"]
diff --git a/lib/un.rb b/lib/un.rb
index 9242357215..796b36fbab 100644
--- a/lib/un.rb
+++ b/lib/un.rb
@@ -256,7 +256,7 @@ def wait_writable
wait = (wait = options[:w]) ? Float(wait) : 0.2
argv.each do |file|
begin
- open(file, "r+b")
+ File.open(file, "r+b") {}
rescue Errno::ENOENT
break
rescue Errno::EACCES => e
@@ -422,7 +422,7 @@ module UN # :nodoc:
messages = {}
store = proc {|msg| messages[cmd] = msg}
end
- open(__FILE__) do |me|
+ File.open(__FILE__) do |me|
while me.gets("##\n")
if help = me.gets("\n\n")
if all or argv.include?(cmd = help[/^#\s*ruby\s.*-e\s+(\w+)/, 1])
diff --git a/lib/unicode_normalize/tables.rb b/lib/unicode_normalize/tables.rb
index 75c4e031e3..7448fad13f 100644
--- a/lib/unicode_normalize/tables.rb
+++ b/lib/unicode_normalize/tables.rb
@@ -150,6 +150,7 @@ module UnicodeNormalize # :nodoc:
"\u{10AE5}\u{10AE6}" \
"\u{10D24}-\u{10D27}" \
"\u{10EAB}\u{10EAC}" \
+ "\u{10EFD}-\u{10EFF}" \
"\u{10F46}-\u{10F50}" \
"\u{10F82}-\u{10F85}" \
"\u{11046}" \
@@ -194,6 +195,7 @@ module UnicodeNormalize # :nodoc:
"\u{11D42}" \
"\u{11D44}\u{11D45}" \
"\u{11D97}" \
+ "\u{11F41}\u{11F42}" \
"\u{16AF0}-\u{16AF4}" \
"\u{16B30}-\u{16B36}" \
"\u{16FF0}\u{16FF1}" \
@@ -209,9 +211,11 @@ module UnicodeNormalize # :nodoc:
"\u{1E01B}-\u{1E021}" \
"\u{1E023}\u{1E024}" \
"\u{1E026}-\u{1E02A}" \
+ "\u{1E08F}" \
"\u{1E130}-\u{1E136}" \
"\u{1E2AE}" \
"\u{1E2EC}-\u{1E2EF}" \
+ "\u{1E4EC}-\u{1E4EF}" \
"\u{1E8D0}-\u{1E8D6}" \
"\u{1E944}-\u{1E94A}" \
"]"
@@ -1457,6 +1461,7 @@ module UnicodeNormalize # :nodoc:
"\u{1D552}-\u{1D6A5}" \
"\u{1D6A8}-\u{1D7CB}" \
"\u{1D7CE}-\u{1D7FF}" \
+ "\u{1E030}-\u{1E06D}" \
"\u{1EE00}-\u{1EE03}" \
"\u{1EE05}-\u{1EE1F}" \
"\u{1EE21}\u{1EE22}" \
@@ -2231,6 +2236,9 @@ module UnicodeNormalize # :nodoc:
"\u{10D27}"=>230,
"\u{10EAB}"=>230,
"\u{10EAC}"=>230,
+ "\u{10EFD}"=>220,
+ "\u{10EFE}"=>220,
+ "\u{10EFF}"=>220,
"\u{10F46}"=>220,
"\u{10F47}"=>220,
"\u{10F48}"=>230,
@@ -2303,6 +2311,8 @@ module UnicodeNormalize # :nodoc:
"\u{11D44}"=>9,
"\u{11D45}"=>9,
"\u{11D97}"=>9,
+ "\u{11F41}"=>9,
+ "\u{11F42}"=>9,
"\u{16AF0}"=>1,
"\u{16AF1}"=>1,
"\u{16AF2}"=>1,
@@ -2389,6 +2399,7 @@ module UnicodeNormalize # :nodoc:
"\u{1E028}"=>230,
"\u{1E029}"=>230,
"\u{1E02A}"=>230,
+ "\u{1E08F}"=>230,
"\u{1E130}"=>230,
"\u{1E131}"=>230,
"\u{1E132}"=>230,
@@ -2401,6 +2412,10 @@ module UnicodeNormalize # :nodoc:
"\u{1E2ED}"=>230,
"\u{1E2EE}"=>230,
"\u{1E2EF}"=>230,
+ "\u{1E4EC}"=>232,
+ "\u{1E4ED}"=>232,
+ "\u{1E4EE}"=>220,
+ "\u{1E4EF}"=>230,
"\u{1E8D0}"=>220,
"\u{1E8D1}"=>220,
"\u{1E8D2}"=>220,
@@ -7931,6 +7946,68 @@ module UnicodeNormalize # :nodoc:
"\u{1D7FD}"=>"7",
"\u{1D7FE}"=>"8",
"\u{1D7FF}"=>"9",
+ "\u{1E030}"=>"\u0430",
+ "\u{1E031}"=>"\u0431",
+ "\u{1E032}"=>"\u0432",
+ "\u{1E033}"=>"\u0433",
+ "\u{1E034}"=>"\u0434",
+ "\u{1E035}"=>"\u0435",
+ "\u{1E036}"=>"\u0436",
+ "\u{1E037}"=>"\u0437",
+ "\u{1E038}"=>"\u0438",
+ "\u{1E039}"=>"\u043A",
+ "\u{1E03A}"=>"\u043B",
+ "\u{1E03B}"=>"\u043C",
+ "\u{1E03C}"=>"\u043E",
+ "\u{1E03D}"=>"\u043F",
+ "\u{1E03E}"=>"\u0440",
+ "\u{1E03F}"=>"\u0441",
+ "\u{1E040}"=>"\u0442",
+ "\u{1E041}"=>"\u0443",
+ "\u{1E042}"=>"\u0444",
+ "\u{1E043}"=>"\u0445",
+ "\u{1E044}"=>"\u0446",
+ "\u{1E045}"=>"\u0447",
+ "\u{1E046}"=>"\u0448",
+ "\u{1E047}"=>"\u044B",
+ "\u{1E048}"=>"\u044D",
+ "\u{1E049}"=>"\u044E",
+ "\u{1E04A}"=>"\uA689",
+ "\u{1E04B}"=>"\u04D9",
+ "\u{1E04C}"=>"\u0456",
+ "\u{1E04D}"=>"\u0458",
+ "\u{1E04E}"=>"\u04E9",
+ "\u{1E04F}"=>"\u04AF",
+ "\u{1E050}"=>"\u04CF",
+ "\u{1E051}"=>"\u0430",
+ "\u{1E052}"=>"\u0431",
+ "\u{1E053}"=>"\u0432",
+ "\u{1E054}"=>"\u0433",
+ "\u{1E055}"=>"\u0434",
+ "\u{1E056}"=>"\u0435",
+ "\u{1E057}"=>"\u0436",
+ "\u{1E058}"=>"\u0437",
+ "\u{1E059}"=>"\u0438",
+ "\u{1E05A}"=>"\u043A",
+ "\u{1E05B}"=>"\u043B",
+ "\u{1E05C}"=>"\u043E",
+ "\u{1E05D}"=>"\u043F",
+ "\u{1E05E}"=>"\u0441",
+ "\u{1E05F}"=>"\u0443",
+ "\u{1E060}"=>"\u0444",
+ "\u{1E061}"=>"\u0445",
+ "\u{1E062}"=>"\u0446",
+ "\u{1E063}"=>"\u0447",
+ "\u{1E064}"=>"\u0448",
+ "\u{1E065}"=>"\u044A",
+ "\u{1E066}"=>"\u044B",
+ "\u{1E067}"=>"\u0491",
+ "\u{1E068}"=>"\u0456",
+ "\u{1E069}"=>"\u0455",
+ "\u{1E06A}"=>"\u045F",
+ "\u{1E06B}"=>"\u04AB",
+ "\u{1E06C}"=>"\uA651",
+ "\u{1E06D}"=>"\u04B1",
"\u{1EE00}"=>"\u0627",
"\u{1EE01}"=>"\u0628",
"\u{1EE02}"=>"\u062C",
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index ca38bec7ec..0c4064a67a 100644
--- a/lib/uri/common.rb
+++ b/lib/uri/common.rb
@@ -19,6 +19,8 @@ module URI
Parser = RFC2396_Parser
RFC3986_PARSER = RFC3986_Parser.new
Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
+ RFC2396_PARSER = RFC2396_Parser.new
+ Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor)
# URI::Parser.new
DEFAULT_PARSER = Parser.new
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 69698c4e2d..f7eed57924 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -1133,17 +1133,16 @@ module URI
base.fragment=(nil)
# RFC2396, Section 5.2, 4)
- if !authority
- base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
- else
- # RFC2396, Section 5.2, 4)
- base.set_path(rel.path) if rel.path
+ if authority
+ base.set_userinfo(rel.userinfo)
+ base.set_host(rel.host)
+ base.set_port(rel.port || base.default_port)
+ base.set_path(rel.path)
+ elsif base.path && rel.path
+ base.set_path(merge_path(base.path, rel.path))
end
# RFC2396, Section 5.2, 7)
- base.set_userinfo(rel.userinfo) if rel.userinfo
- base.set_host(rel.host) if rel.host
- base.set_port(rel.port) if rel.port
base.query = rel.query if rel.query
base.fragment=(rel.fragment) if rel.fragment
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index 76a8f99fd4..00c66cf042 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -497,8 +497,8 @@ module URI
ret = {}
# for URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index 3e07de4805..9b1663dbb6 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -2,9 +2,8 @@
module URI
class RFC3986_Parser # :nodoc:
# URI defined in RFC3986
- # this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -101,7 +100,7 @@ module URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/uri/version.rb b/lib/uri/version.rb
index 82188e25ad..c93c97cf6f 100644
--- a/lib/uri/version.rb
+++ b/lib/uri/version.rb
@@ -1,6 +1,6 @@
module URI
# :stopdoc:
- VERSION_CODE = '001100'.freeze
+ VERSION_CODE = '001204'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/weakref.rb b/lib/weakref.rb
index 78aad1f96e..2bbadf68f9 100644
--- a/lib/weakref.rb
+++ b/lib/weakref.rb
@@ -17,7 +17,7 @@ require "delegate"
#
class WeakRef < Delegator
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
##
# RefError is raised when a referenced object has been recycled by the
diff --git a/lib/yaml/yaml.gemspec b/lib/yaml/yaml.gemspec
index e8f20b8c2e..80554d8a7a 100644
--- a/lib/yaml/yaml.gemspec
+++ b/lib/yaml/yaml.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "yaml"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
diff --git a/libexec/bundle b/libexec/bundle
index 73a6397e77..90c62627f8 100755
--- a/libexec/bundle
+++ b/libexec/bundle
@@ -10,15 +10,12 @@ end
base_path = File.expand_path("../lib", __dir__)
if File.exist?(base_path)
- require_relative "../lib/bundler"
-else
- require "bundler"
+ $LOAD_PATH.unshift(base_path)
end
-# Workaround for non-activated bundler spec due to missing https://github.com/rubygems/rubygems/commit/4e306d7bcdee924b8d80ca9db6125aa59ee4e5a3
-gem "bundler", Bundler::VERSION if Gem.rubygems_version < Gem::Version.new("2.6.2")
+require "bundler"
-if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::Version.new("2.6.a") && !ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"]
+if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::Version.new("2.7.a") && !ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"]
Bundler.ui.warn \
"Your RubyGems version (#{Gem::VERSION}) has a bug that prevents " \
"`required_ruby_version` from working for Bundler. Any scripts that use " \
@@ -27,18 +24,10 @@ if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::V
"and silence this warning by running `gem update --system 3.2.3`"
end
-if File.exist?(base_path)
- require_relative "../lib/bundler/friendly_errors"
-else
- require "bundler/friendly_errors"
-end
+require "bundler/friendly_errors"
Bundler.with_friendly_errors do
- if File.exist?(base_path)
- require_relative "../lib/bundler/cli"
- else
- require "bundler/cli"
- end
+ require "bundler/cli"
# Allow any command to use --help flag to show help for that command
help_flags = %w[--help -h]
diff --git a/libexec/erb b/libexec/erb
index ded76991dc..4381671f25 100755
--- a/libexec/erb
+++ b/libexec/erb
@@ -74,11 +74,6 @@ class ERB
$DEBUG = true
when '-r' # require
require ARGV.req_arg
- when '-S' # security level
- warn 'warning: -S option of erb command is deprecated. Please do not use this.'
- arg = ARGV.req_arg
- raise "invalid safe_level #{arg.dump}" unless arg =~ /\A[0-1]\z/
- safe_level = arg.to_i
when '-T' # trim mode
arg = ARGV.req_arg
if arg == '-'
@@ -127,12 +122,7 @@ EOU
filename = $FILENAME
exit 2 unless src
trim = trim_mode_opt(trim_mode, disable_percent)
- if safe_level.nil?
- erb = factory.new(src, trim_mode: trim)
- else
- # [deprecated] This will be removed at Ruby 2.7.
- erb = factory.new(src, safe_level, trim_mode: trim)
- end
+ erb = factory.new(src, trim_mode: trim)
erb.filename = filename
if output
if number
diff --git a/libexec/syntax_suggest b/libexec/syntax_suggest
new file mode 100755
index 0000000000..e4a0b0b658
--- /dev/null
+++ b/libexec/syntax_suggest
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+require_relative "../lib/syntax_suggest/api"
+
+SyntaxSuggest::Cli.new(
+ argv: ARGV
+).call
diff --git a/load.c b/load.c
index 62b9b2d9bd..ecb21faa72 100644
--- a/load.c
+++ b/load.c
@@ -164,6 +164,12 @@ get_loaded_features_realpaths(rb_vm_t *vm)
}
static VALUE
+get_loaded_features_realpath_map(rb_vm_t *vm)
+{
+ return vm->loaded_features_realpath_map;
+}
+
+static VALUE
get_LOADED_FEATURES(ID _x, VALUE *_y)
{
return get_loaded_features(GET_VM());
@@ -361,7 +367,10 @@ get_loaded_features_index(rb_vm_t *vm)
st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
VALUE realpaths = vm->loaded_features_realpaths;
+ VALUE realpath_map = vm->loaded_features_realpath_map;
+ VALUE previous_realpath_map = rb_hash_dup(realpath_map);
rb_hash_clear(realpaths);
+ rb_hash_clear(realpath_map);
features = vm->loaded_features;
for (i = 0; i < RARRAY_LEN(features); i++) {
VALUE entry, as_str;
@@ -378,9 +387,14 @@ get_loaded_features_index(rb_vm_t *vm)
long j = RARRAY_LEN(features);
for (i = 0; i < j; i++) {
VALUE as_str = rb_ary_entry(features, i);
- VALUE realpath = rb_check_realpath(Qnil, as_str, NULL);
- if (NIL_P(realpath)) realpath = as_str;
- rb_hash_aset(realpaths, rb_fstring(realpath), Qtrue);
+ VALUE realpath = rb_hash_aref(previous_realpath_map, as_str);
+ if (NIL_P(realpath)) {
+ realpath = rb_check_realpath(Qnil, as_str, NULL);
+ if (NIL_P(realpath)) realpath = as_str;
+ realpath = rb_fstring(realpath);
+ }
+ rb_hash_aset(realpaths, realpath, Qtrue);
+ rb_hash_aset(realpath_map, as_str, realpath);
}
}
return vm->loaded_features_index;
@@ -553,7 +567,7 @@ rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expa
loading_tbl = get_loading_table(vm);
f = 0;
- if (!expanded) {
+ if (!expanded && !rb_is_absolute_path(feature)) {
struct loaded_feature_searching fs;
fs.name = feature;
fs.len = len;
@@ -681,6 +695,8 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
if (!iseq) {
+ rb_execution_context_t *ec = GET_EC();
+ VALUE v = rb_vm_push_frame_fname(ec, fname);
rb_ast_t *ast;
VALUE parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
@@ -688,6 +704,8 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"),
fname, rb_realpath_internal(Qnil, fname, 1), NULL);
rb_ast_dispose(ast);
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
}
rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
rb_iseq_eval(iseq);
@@ -846,15 +864,8 @@ load_lock(rb_vm_t *vm, const char *ftptr, bool warn)
st_insert(loading_tbl, (st_data_t)ftptr, data);
return (char *)ftptr;
}
- else if (imemo_type_p(data, imemo_memo)) {
- struct MEMO *memo = MEMO_CAST(data);
- void (*init)(void) = memo->u3.func;
- data = (st_data_t)rb_thread_shield_new();
- st_insert(loading_tbl, (st_data_t)ftptr, data);
- (*init)();
- return (char *)"";
- }
- if (warn) {
+
+ if (warn && rb_thread_shield_owned((VALUE)data)) {
VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
rb_backtrace_each(rb_str_append, warning);
rb_warning("%"PRIsVALUE, warning);
@@ -896,6 +907,7 @@ load_unlock(rb_vm_t *vm, const char *ftptr, int done)
}
}
+static VALUE rb_require_string_internal(VALUE fname);
/*
* call-seq:
@@ -958,7 +970,7 @@ rb_f_require_relative(VALUE obj, VALUE fname)
rb_loaderror("cannot infer basepath");
}
base = rb_file_dirname(base);
- return rb_require_string(rb_file_absolute_path(fname, base));
+ return rb_require_string_internal(rb_file_absolute_path(fname, base));
}
typedef int (*feature_func)(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn);
@@ -1021,16 +1033,35 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
}
tmp = fname;
type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
+
+ // Check if it's a statically linked extension when
+ // not already a feature and not found as a dynamic library.
+ if (!ft && type != 1 && vm->static_ext_inits) {
+ VALUE lookup_name = tmp;
+ // Append ".so" if not already present so for example "etc" can find "etc.so".
+ // We always register statically linked extensions with a ".so" extension.
+ // See encinit.c and extinit.c (generated at build-time).
+ if (!ext) {
+ lookup_name = rb_str_dup(lookup_name);
+ rb_str_cat_cstr(lookup_name, ".so");
+ }
+ ftptr = RSTRING_PTR(lookup_name);
+ if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
+ *path = rb_filesystem_str_new_cstr(ftptr);
+ return 's';
+ }
+ }
+
switch (type) {
case 0:
if (ft)
- goto statically_linked;
+ goto feature_present;
ftptr = RSTRING_PTR(tmp);
return rb_feature_p(vm, ftptr, 0, FALSE, TRUE, 0);
default:
if (ft) {
- goto statically_linked;
+ goto feature_present;
}
/* fall through */
case 1:
@@ -1041,7 +1072,7 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
}
return type ? 's' : 'r';
- statically_linked:
+ feature_present:
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return ft;
}
@@ -1059,6 +1090,19 @@ load_ext(VALUE path)
return (VALUE)dln_load(RSTRING_PTR(path));
}
+static bool
+run_static_ext_init(rb_vm_t *vm, const char *feature)
+{
+ st_data_t key = (st_data_t)feature;
+ st_data_t init_func;
+
+ if (vm->static_ext_inits && st_delete(vm->static_ext_inits, &key, &init_func)) {
+ ((void (*)(void))init_func)();
+ return true;
+ }
+ return false;
+}
+
static int
no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
@@ -1133,10 +1177,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
volatile VALUE saved_path;
volatile VALUE realpath = 0;
VALUE realpaths = get_loaded_features_realpaths(th->vm);
+ VALUE realpath_map = get_loaded_features_realpath_map(th->vm);
volatile bool reset_ext_config = false;
struct rb_ext_config prev_ext_config;
- fname = rb_get_path(fname);
path = rb_str_encode_ospath(fname);
RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
saved_path = path;
@@ -1160,6 +1204,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
else if (!*ftptr) {
result = TAG_RETURN;
}
+ else if (found == 's' && run_static_ext_init(th->vm, RSTRING_PTR(path))) {
+ result = TAG_RETURN;
+ }
else if (RTEST(rb_hash_aref(realpaths,
realpath = rb_realpath_internal(Qnil, path, 1)))) {
result = 0;
@@ -1219,7 +1266,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
rb_provide_feature(th2->vm, path);
VALUE real = realpath;
if (real) {
- rb_hash_aset(realpaths, rb_fstring(real), Qtrue);
+ real = rb_fstring(real);
+ rb_hash_aset(realpaths, real, Qtrue);
+ rb_hash_aset(realpath_map, path, real);
}
}
ec->errinfo = saved.errinfo;
@@ -1257,6 +1306,12 @@ ruby_require_internal(const char *fname, unsigned int len)
VALUE
rb_require_string(VALUE fname)
{
+ return rb_require_string_internal(FilePathValue(fname));
+}
+
+static VALUE
+rb_require_string_internal(VALUE fname)
+{
rb_execution_context_t *ec = GET_EC();
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
@@ -1273,7 +1328,9 @@ rb_require_string(VALUE fname)
VALUE
rb_require(const char *fname)
{
- return rb_require_string(rb_str_new_cstr(fname));
+ struct RString fake;
+ VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
+ return rb_require_string_internal(str);
}
static int
@@ -1285,21 +1342,29 @@ register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing
rb_warn("%s is already registered", name);
}
else {
- *value = (st_data_t)MEMO_NEW(0, 0, init);
- *key = (st_data_t)ruby_strdup(name);
+ *value = (st_data_t)init;
}
return ST_CONTINUE;
}
-RUBY_FUNC_EXPORTED void
+// Private API for statically linked extensions.
+// Used with the ext/Setup file, the --with-setup and
+// --with-static-linked-ext configuration option, etc.
+void
ruby_init_ext(const char *name, void (*init)(void))
{
+ st_table *inits_table;
rb_vm_t *vm = GET_VM();
- st_table *loading_tbl = get_loading_table(vm);
if (feature_provided(vm, name, 0))
return;
- st_update(loading_tbl, (st_data_t)name, register_init_ext, (st_data_t)init);
+
+ inits_table = vm->static_ext_inits;
+ if (!inits_table) {
+ inits_table = st_init_strtable();
+ vm->static_ext_inits = inits_table;
+ }
+ st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
}
/*
@@ -1439,6 +1504,8 @@ Init_load(void)
vm->loaded_features_index = st_init_numtable();
vm->loaded_features_realpaths = rb_hash_new();
rb_obj_hide(vm->loaded_features_realpaths);
+ vm->loaded_features_realpath_map = rb_hash_new();
+ rb_obj_hide(vm->loaded_features_realpath_map);
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
diff --git a/main.c b/main.c
index 0d0ec147cd..c87355e46e 100644
--- a/main.c
+++ b/main.c
@@ -43,6 +43,12 @@ int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv);
#define rb_main(argc, argv) rb_wasm_rt_start(rb_main, argc, argv)
#endif
+#ifdef _WIN32
+#define main(argc, argv) w32_main(argc, argv)
+static int main(int argc, char **argv);
+int wmain(void) {return main(0, NULL);}
+#endif
+
int
main(int argc, char **argv)
{
diff --git a/man/irb.1 b/man/irb.1
index 50db899177..c589c99c78 100644
--- a/man/irb.1
+++ b/man/irb.1
@@ -173,8 +173,19 @@ The default value is 16.
.El
.Pp
.Sh ENVIRONMENT
-.Bl -tag -compact
+.Bl -tag -compact -width "XDG_CONFIG_HOME"
+.It Ev IRB_LANG
+The locale used for
+.Nm .
+.Pp
.It Ev IRBRC
+The path to the personal initialization file.
+.Pp
+.It Ev XDG_CONFIG_HOME
+.Nm
+respects XDG_CONFIG_HOME. If this is set, load
+.Pa $XDG_CONFIG_HOME/irb/irbrc
+as a personal initialization file.
.Pp
.El
.Pp
@@ -186,7 +197,17 @@ depends on same variables as
.Sh FILES
.Bl -tag -compact
.It Pa ~/.irbrc
-Personal irb initialization.
+Personal irb initialization. If
+.Ev IRBRC
+is set, read
+.Pa $IRBRC
+instead. If
+.Ev IRBRC
+is not set and
+.Ev XDG_CONFIG_HOME
+is set,
+.Pa $XDG_CONFIG_HOME/irb/irbrc
+is loaded.
.Pp
.El
.Pp
diff --git a/marshal.c b/marshal.c
index 1eeebf7729..4ea4255971 100644
--- a/marshal.c
+++ b/marshal.c
@@ -39,6 +39,8 @@
#include "ruby/st.h"
#include "ruby/util.h"
#include "builtin.h"
+#include "shape.h"
+#include "ruby/internal/attr/nonstring.h"
#define BITSPERSHORT (2*CHAR_BIT)
#define SHORTMASK ((1<<BITSPERSHORT)-1)
@@ -522,7 +524,7 @@ hash_each(VALUE key, VALUE value, VALUE v)
#define SINGLETON_DUMP_UNABLE_P(klass) \
(rb_id_table_size(RCLASS_M_TBL(klass)) > 0 || \
- (RCLASS_IV_TBL(klass) && RCLASS_IV_TBL(klass)->num_entries > 1))
+ rb_ivar_count(klass) > 1)
static void
w_extended(VALUE klass, struct dump_arg *arg, int check)
@@ -622,10 +624,6 @@ w_obj_each(st_data_t key, st_data_t val, st_data_t a)
}
return ST_CONTINUE;
}
- if (!ivarg->num_ivar) {
- rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance",
- CLASS_OF(arg->obj));
- }
--ivarg->num_ivar;
w_symbol(ID2SYM(id), arg->arg);
w_object(value, arg->arg, arg->limit);
@@ -720,12 +718,27 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj)
static void
w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg)
{
+ shape_id_t shape_id = rb_shape_get_shape_id(arg->obj);
struct w_ivar_arg ivarg = {arg, num};
if (!num) return;
rb_ivar_foreach(obj, w_obj_each, (st_data_t)&ivarg);
- if (ivarg.num_ivar) {
- rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance",
- CLASS_OF(arg->obj));
+
+ if (shape_id != rb_shape_get_shape_id(arg->obj)) {
+ rb_shape_t * expected_shape = rb_shape_get_shape_by_id(shape_id);
+ rb_shape_t * actual_shape = rb_shape_get_shape(arg->obj);
+
+ // If the shape tree got _shorter_ then we probably removed an IV
+ // If the shape tree got longer, then we probably added an IV.
+ // The exception message might not be accurate when someone adds and
+ // removes the same number of IVs, but they will still get an exception
+ if (rb_shape_depth(expected_shape) > rb_shape_depth(actual_shape)) {
+ rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance",
+ CLASS_OF(arg->obj));
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance",
+ CLASS_OF(arg->obj));
+ }
}
}
@@ -741,7 +754,7 @@ w_ivar(st_index_t num, VALUE ivobj, VALUE encname, struct dump_call_arg *arg)
w_object(Qtrue, arg->arg, limit);
num--;
}
- if (ivobj != Qundef && num) {
+ if (!UNDEF_P(ivobj) && num) {
w_ivar_each(ivobj, num, arg);
}
}
@@ -756,6 +769,7 @@ w_objivar(VALUE obj, struct dump_call_arg *arg)
w_ivar_each(obj, num, arg);
}
+#if SIZEOF_LONG > 4
// Optimized dump for fixnum larger than 31-bits
static void
w_bigfixnum(VALUE obj, struct dump_arg *arg)
@@ -803,6 +817,7 @@ w_bigfixnum(VALUE obj, struct dump_arg *arg)
RUBY_ASSERT(num == 0);
}
+#endif
static void
w_remember(VALUE obj, struct dump_arg *arg)
@@ -926,7 +941,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
arg->compat_tbl = rb_init_identtable();
}
st_insert(arg->compat_tbl, (st_data_t)obj, (st_data_t)real_obj);
- if (obj != real_obj && ivobj == Qundef) hasiv = 0;
+ if (obj != real_obj && UNDEF_P(ivobj)) hasiv = 0;
}
}
if (hasiv) w_byte(TYPE_IVAR, arg);
@@ -1487,7 +1502,7 @@ name_equal(const char *name, size_t nlen, const char *p, long l)
static int
sym2encidx(VALUE sym, VALUE val)
{
- static const char name_encoding[8] = "encoding";
+ RBIMPL_ATTR_NONSTRING() static const char name_encoding[8] = "encoding";
const char *p;
long l;
if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return -1;
@@ -1552,7 +1567,7 @@ r_symreal(struct load_arg *arg, int ivar)
}
if (idx > 0) {
rb_enc_associate_index(s, idx);
- if (rb_enc_str_coderange(s) == ENC_CODERANGE_BROKEN) {
+ if (is_broken_string(s)) {
rb_raise(rb_eArgError, "invalid byte sequence in %s: %+"PRIsVALUE,
rb_enc_name(rb_enc_from_index(idx)), s);
}
@@ -1789,6 +1804,20 @@ r_object0(struct load_arg *arg, bool partial, int *ivp, VALUE extmod)
return r_object_for(arg, partial, ivp, extmod, type);
}
+static int
+r_move_ivar(st_data_t k, st_data_t v, st_data_t d)
+{
+ ID key = (ID)k;
+ VALUE value = (VALUE)v;
+ VALUE dest = (VALUE)d;
+
+ if (rb_is_instance_id(key)) {
+ rb_ivar_set(dest, key, value);
+ return ST_DELETE;
+ }
+ return ST_CONTINUE;
+}
+
static VALUE
r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type)
{
@@ -1812,7 +1841,6 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
case TYPE_IVAR:
{
int ivar = TRUE;
-
v = r_object0(arg, true, &ivar, extmod);
if (ivar) r_ivar(v, NULL, arg);
v = r_leave(v, arg, partial);
@@ -1957,7 +1985,8 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
if (sign == '-') {
v = rb_int_uminus(v);
}
- } else {
+ }
+ else {
data = r_bytes0(len * 2, arg);
v = rb_integer_unpack(RSTRING_PTR(data), len, 2, 0,
INTEGER_PACK_LITTLE_ENDIAN | (sign == '-' ? INTEGER_PACK_NEGATIVE : 0));
@@ -2004,7 +2033,10 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
}
rb_str_set_len(str, dst - ptr);
}
- v = r_entry0(rb_reg_new_str(str, options), idx, arg);
+ VALUE regexp = rb_reg_new_str(str, options);
+ rb_ivar_foreach(str, r_move_ivar, regexp);
+
+ v = r_entry0(regexp, idx, arg);
v = r_leave(v, arg, partial);
}
break;
@@ -2246,7 +2278,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
break;
}
- if (v == Qundef) {
+ if (UNDEF_P(v)) {
rb_raise(rb_eArgError, "dump format error (bad link)");
}
diff --git a/method.h b/method.h
index 16d212a1c8..ec89bf26bb 100644
--- a/method.h
+++ b/method.h
@@ -101,8 +101,9 @@ static inline void
METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
{
dst->flags =
- (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
- (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
+ (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2
+ |IMEMO_FL_USER3)) |
+ (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3));
}
typedef enum {
@@ -178,9 +179,9 @@ typedef struct rb_method_optimized {
struct rb_method_definition_struct {
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
unsigned int iseq_overload: 1;
- int alias_count : 27;
- int complemented_count : 28;
unsigned int no_redef_warning: 1;
+ unsigned int aliased : 1;
+ int reference_count : 28;
union {
rb_method_iseq_t iseq;
@@ -213,7 +214,7 @@ void rb_add_method_optimized(VALUE klass, ID mid, enum method_optimized_type, un
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
-rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def);
+rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, rb_method_definition_t *def);
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
@@ -226,6 +227,7 @@ const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE
RUBY_SYMBOL_EXPORT_END
const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id);
+const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID id);
const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
diff --git a/mini_builtin.c b/mini_builtin.c
index 8c8cf66263..c263d1ee71 100644
--- a/mini_builtin.c
+++ b/mini_builtin.c
@@ -36,7 +36,7 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta
FALSE, /* unsigned int coverage_enabled; */
0, /* int debug_level; */
};
- const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, INT2FIX(0), NULL, 0, ISEQ_TYPE_TOP, &optimization);
+ const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization);
GET_VM()->builtin_function_table = NULL;
rb_ast_dispose(ast);
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
index 595d54dfab..9ef9d3967b 100755
--- a/misc/lldb_cruby.py
+++ b/misc/lldb_cruby.py
@@ -418,6 +418,7 @@ def lldb_inspect(debugger, target, result, val):
elif flType == RUBY_T_IMEMO:
# I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals()
imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK
+
print("T_IMEMO: ", file=result)
append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result)
append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
diff --git a/misc/lldb_rb/rb_base_command.py b/misc/lldb_rb/rb_base_command.py
index bf98b67612..de90a1617c 100644
--- a/misc/lldb_rb/rb_base_command.py
+++ b/misc/lldb_rb/rb_base_command.py
@@ -19,8 +19,8 @@ class RbBaseCommand:
imemo_types = target.FindFirstType("enum imemo_type")
- for member in imemo_types.GetEnumMembers():
- g[member.GetName()] = member.GetValueAsUnsigned()
+ #for member in imemo_types.GetEnumMembers():
+ # g[member.GetName()] = member.GetValueAsUnsigned()
for enum in target.FindFirstGlobalVariable("ruby_dummy_gdb_enums"):
enum = enum.GetType()
diff --git a/mjit.c b/mjit.c
index a873b3d9f8..3200ee2621 100644
--- a/mjit.c
+++ b/mjit.c
@@ -1,8 +1,9 @@
/**********************************************************************
- mjit.c - MRI method JIT compiler functions for Ruby's main thread
+ mjit.c - MRI method JIT compiler functions
Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
@@ -88,7 +89,7 @@
#include "vm_core.h"
#include "vm_callinfo.h"
#include "mjit.h"
-#include "mjit_unit.h"
+#include "mjit_c.h"
#include "gc.h"
#include "ruby_assert.h"
#include "ruby/debug.h"
@@ -123,23 +124,11 @@
#define MJIT_TMP_PREFIX "_ruby_mjit_"
-// Linked list of struct rb_mjit_unit.
-struct rb_mjit_unit_list {
- struct ccan_list_head head;
- int length; // the list length
-};
-
extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_initialize(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
-extern void rb_native_cond_initialize(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_signal(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
-extern void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
-
// process.c
extern void mjit_add_waiting_pid(rb_vm_t *vm, rb_pid_t pid);
@@ -153,6 +142,15 @@ bool mjit_enabled = false;
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
// and `mjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
bool mjit_call_p = false;
+// A flag to communicate that mjit_call_p should be disabled while it's temporarily false.
+bool mjit_cancel_p = false;
+// There's an ISEQ in unit_queue whose total_calls reached 2 * call_threshold.
+// If this is true, check_unit_queue will start compiling ISEQs in unit_queue.
+static bool mjit_compile_p = false;
+// The actual number of units in active_units
+static int active_units_length = 0;
+// The actual number of units in compact_units
+static int compact_units_length = 0;
// Priority queue of iseqs waiting for JIT compilation.
// This variable is a pointer to head unit of the queue.
@@ -167,20 +165,6 @@ static struct rb_mjit_unit_list stale_units = { CCAN_LIST_HEAD_INIT(stale_units.
static int current_unit_num;
// A mutex for conitionals and critical sections.
static rb_nativethread_lock_t mjit_engine_mutex;
-// A thread conditional to wake up `mjit_finish` at the end of PCH thread.
-static rb_nativethread_cond_t mjit_pch_wakeup;
-// A thread conditional to wake up the client if there is a change in
-// executed unit status.
-static rb_nativethread_cond_t mjit_client_wakeup;
-// A thread conditional to wake up a worker if there we have something
-// to add or we need to stop MJIT engine.
-static rb_nativethread_cond_t mjit_worker_wakeup;
-// A thread conditional to wake up workers if at the end of GC.
-static rb_nativethread_cond_t mjit_gc_wakeup;
-// The times when unload_units is requested. unload_units is called after some requests.
-static int unload_requests = 0;
-// The total number of unloaded units.
-static int total_unloads = 0;
// Set to true to stop worker.
static bool stop_worker_p;
// Set to true if worker is stopped.
@@ -306,11 +290,6 @@ mjit_warning(const char *format, ...)
static void
add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
{
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_unit_queue, list == &unit_queue);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_active_units, list == &active_units);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_compact_units, list == &compact_units);
- (void)RB_DEBUG_COUNTER_INC_IF(mjit_length_stale_units, list == &stale_units);
-
ccan_list_add_tail(&list->head, &unit->unode);
list->length++;
}
@@ -318,13 +297,6 @@ add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
static void
remove_from_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
{
-#if USE_DEBUG_COUNTER
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_unit_queue, -1, list == &unit_queue);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_active_units, -1, list == &active_units);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_compact_units, -1, list == &compact_units);
- rb_debug_counter_add(RB_DEBUG_COUNTER_mjit_length_stale_units, -1, list == &stale_units);
-#endif
-
ccan_list_del(&unit->unode);
list->length--;
}
@@ -343,14 +315,14 @@ remove_file(const char *filename)
// 3) Freeing lists on `mjit_finish()`.
//
// `jit_func` value does not matter for 1 and 3 since the unit won't be used anymore.
-// For the situation 2, this sets the ISeq's JIT state to NOT_COMPILED_JIT_ISEQ_FUNC
+// For the situation 2, this sets the ISeq's JIT state to MJIT_FUNC_FAILED
// to prevent the situation that the same methods are continuously compiled.
static void
free_unit(struct rb_mjit_unit *unit)
{
if (unit->iseq) { // ISeq is not GCed
- ISEQ_BODY(unit->iseq)->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- ISEQ_BODY(unit->iseq)->jit_unit = NULL;
+ ISEQ_BODY(unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED;
+ ISEQ_BODY(unit->iseq)->mjit_unit = NULL;
}
if (unit->cc_entries) {
void *entries = (void *)unit->cc_entries;
@@ -359,7 +331,7 @@ free_unit(struct rb_mjit_unit *unit)
if (unit->handle && dlclose(unit->handle)) { // handle is NULL if it's in queue
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
}
- free(unit);
+ xfree(unit);
}
// Start a critical section. Use message `msg` to print debug info at `level`.
@@ -566,11 +538,11 @@ remove_so_file(const char *so_file, struct rb_mjit_unit *unit)
// Print _mjitX, but make a human-readable funcname when --mjit-debug is used
static void
-sprint_funcname(char *funcname, const struct rb_mjit_unit *unit)
+sprint_funcname(char *funcname, size_t funcname_size, const struct rb_mjit_unit *unit)
{
const rb_iseq_t *iseq = unit->iseq;
if (iseq == NULL || (!mjit_opts.debug && !mjit_opts.debug_flags)) {
- sprintf(funcname, "_mjit%d", unit->id);
+ snprintf(funcname, funcname_size, "_mjit%d", unit->id);
return;
}
@@ -589,7 +561,7 @@ sprint_funcname(char *funcname, const struct rb_mjit_unit *unit)
if (!strcmp(method, "[]=")) method = "ASET";
// Print and normalize
- sprintf(funcname, "_mjit%d_%s_%s", unit->id, path, method);
+ snprintf(funcname, funcname_size, "_mjit%d_%s_%s", unit->id, path, method);
for (size_t i = 0; i < strlen(funcname); i++) {
char c = funcname[i];
if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_')) {
@@ -614,6 +586,7 @@ make_pch(void)
{
const char *rest_args[] = {
# ifdef __clang__
+ "-Xclang",
"-emit-pch",
"-c",
# endif
@@ -645,7 +618,7 @@ make_pch(void)
}
static int
-compile_c_to_so(const char *c_file, const char *so_file)
+c_compile(const char *c_file, const char *so_file)
{
const char *so_args[] = {
"-o", so_file,
@@ -674,12 +647,32 @@ compile_c_to_so(const char *c_file, const char *so_file)
return exit_code;
}
+static int
+c_compile_unit(struct rb_mjit_unit *unit)
+{
+ static const char c_ext[] = ".c";
+ static const char so_ext[] = DLEXT;
+ char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
+
+ sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
+ sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
+
+ return c_compile(c_file, so_file);
+}
+
static void compile_prelude(FILE *f);
-// Compile all JIT code into a single .c file
static bool
-mjit_compact(char* c_file)
+mjit_batch(struct rb_mjit_unit *unit)
{
+ VM_ASSERT(unit->type == MJIT_UNIT_BATCH);
+ static const char c_ext[] = ".c";
+ static const char so_ext[] = DLEXT;
+ char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
+
+ sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
+ sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
+
FILE *f;
int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
@@ -691,31 +684,21 @@ mjit_compact(char* c_file)
compile_prelude(f);
- // This entire loop lock GC so that we do not need to consider a case that
- // ISeq is GC-ed in a middle of re-compilation. It takes 3~4ms with 100 methods
- // on my machine. It's not too bad compared to compilation time of C (7200~8000ms),
- // but it might be larger if we use a larger --jit-max-cache.
- //
- // TODO: Consider using a more granular lock after we implement inlining across
- // compacted functions (not done yet).
bool success = true;
struct rb_mjit_unit *child_unit = 0;
- ccan_list_for_each(&active_units.head, child_unit, unode) {
+ ccan_list_for_each(&unit->units.head, child_unit, unode) {
if (!success) continue;
- if (ISEQ_BODY(child_unit->iseq)->jit_unit == NULL) continue; // Sometimes such units are created. TODO: Investigate why
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
char funcname[MAXPATHLEN];
- sprint_funcname(funcname, child_unit);
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
- long iseq_lineno = 0;
- if (FIXNUM_P(ISEQ_BODY(child_unit->iseq)->location.first_lineno))
- // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG.
- iseq_lineno = FIX2LONG(ISEQ_BODY(child_unit->iseq)->location.first_lineno);
+ int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
const char *sep = "@";
const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
if (!iseq_label) iseq_label = sep = "";
- fprintf(f, "\n/* %s%s%s:%ld */\n", iseq_label, sep, iseq_path, iseq_lineno);
+ fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
}
@@ -725,9 +708,10 @@ mjit_compact(char* c_file)
// Compile all cached .c files and build a single .so file. Reload all JIT func from it.
// This improves the code locality for better performance in terms of iTLB and iCache.
-static int
-mjit_compact_unit(struct rb_mjit_unit *unit)
+static bool
+mjit_compact(struct rb_mjit_unit *unit)
{
+ VM_ASSERT(unit->type == MJIT_UNIT_COMPACT);
static const char c_ext[] = ".c";
static const char so_ext[] = DLEXT;
char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
@@ -735,86 +719,130 @@ mjit_compact_unit(struct rb_mjit_unit *unit)
sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
- bool success = mjit_compact(c_file);
- if (success) {
- return compile_c_to_so(c_file, so_file);
+ FILE *f;
+ int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
+ if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
+ int e = errno;
+ if (fd >= 0) (void)close(fd);
+ verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
+ return false;
}
- return 1;
-}
-static pid_t
-start_mjit_compact(struct rb_mjit_unit *unit)
-{
- rb_vm_t *vm = GET_VM();
- rb_native_mutex_lock(&vm->waitpid_lock);
-
- pid_t pid = rb_fork();
- if (pid == 0) {
- rb_native_mutex_unlock(&vm->waitpid_lock);
+ compile_prelude(f);
- int exit_code = mjit_compact_unit(unit);
- exit(exit_code);
+ bool success = true;
+ compact_units_length = 0;
+ struct rb_mjit_unit *batch_unit = 0, *child_unit = 0;
+ ccan_list_for_each(&active_units.head, batch_unit, unode) {
+ ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
+ if (!success) continue;
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
+
+ char funcname[MAXPATHLEN];
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+
+ int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
+ const char *sep = "@";
+ const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
+ const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
+ if (!iseq_label) iseq_label = sep = "";
+ fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
+ success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
+ compact_units_length++;
+ }
}
- else {
- mjit_add_waiting_pid(vm, pid);
- rb_native_mutex_unlock(&vm->waitpid_lock);
- return pid;
- }
+ fclose(f);
+ return success;
}
static void
-load_compact_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
+load_batch_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
{
- struct rb_mjit_unit *cur = 0;
double end_time = real_ms_time();
void *handle = dlopen(so_file, RTLD_NOW);
if (handle == NULL) {
- mjit_warning("failure in loading code from compacted '%s': %s", so_file, dlerror());
- free(unit);
+ mjit_warning("failure in loading code from batched '%s': %s", so_file, dlerror());
+ xfree(unit);
return;
}
unit->handle = handle;
- // lazily dlclose handle (and .so file for win32) on `mjit_finish()`.
- add_to_list(unit, &compact_units);
+ // lazily dlclose handle on `mjit_finish()`.
+ add_to_list(unit, &active_units);
+ active_units_length += unit->units.length;
if (!mjit_opts.save_temps)
remove_so_file(so_file, unit);
- ccan_list_for_each(&active_units.head, cur, unode) {
- void *func;
+ struct rb_mjit_unit *child_unit = 0;
+ ccan_list_for_each(&unit->units.head, child_unit, unode) {
char funcname[MAXPATHLEN];
- sprint_funcname(funcname, cur);
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+ void *func;
if ((func = dlsym(handle, funcname)) == NULL) {
- mjit_warning("skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
+ mjit_warning("skipping to load '%s' from '%s': %s", funcname, so_file, dlerror());
continue;
}
- if (cur->iseq) { // Check whether GCed or not
+ if (child_unit->iseq) { // Check whether GCed or not
// Usage of jit_code might be not in a critical section.
- MJIT_ATOMIC_SET(ISEQ_BODY(cur->iseq)->jit_func, (mjit_func_t)func);
+ const rb_iseq_t *iseq = child_unit->iseq;
+ MJIT_ATOMIC_SET(ISEQ_BODY(iseq)->jit_func, (jit_func_t)func);
+
+ verbose(1, "JIT success: %s@%s:%d",
+ RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
+ RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
+ }
+ else {
+ verbose(1, "JIT skip: A compiled method has been GCed");
}
}
- verbose(1, "JIT compaction (%.1fms): Compacted %d methods %s -> %s", end_time - current_cc_ms, active_units.length, c_file, so_file);
+ verbose(1, "JIT batch (%.1fms): Batched %d methods %s -> %s", end_time - current_cc_ms, unit->units.length, c_file, so_file);
}
-static void *
-load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
+static void
+load_compact_funcs_from_so(struct rb_mjit_unit *unit, char *c_file, char *so_file)
{
- void *handle, *func;
+ double end_time = real_ms_time();
- handle = dlopen(so_file, RTLD_NOW);
+ void *handle = dlopen(so_file, RTLD_NOW);
if (handle == NULL) {
- mjit_warning("failure in loading code from '%s': %s", so_file, dlerror());
- return (void *)NOT_COMPILED_JIT_ISEQ_FUNC;
+ mjit_warning("failure in loading code from compacted '%s': %s", so_file, dlerror());
+ xfree(unit);
+ return;
}
-
- func = dlsym(handle, funcname);
unit->handle = handle;
- return func;
+
+ // lazily dlclose handle on `mjit_finish()`.
+ add_to_list(unit, &compact_units);
+
+ if (!mjit_opts.save_temps)
+ remove_so_file(so_file, unit);
+
+ struct rb_mjit_unit *batch_unit = 0, *child_unit = 0;
+ ccan_list_for_each(&active_units.head, batch_unit, unode) {
+ ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
+ if (child_unit->iseq == NULL) continue; // ISEQ is GCed
+
+ char funcname[MAXPATHLEN];
+ sprint_funcname(funcname, sizeof(funcname), child_unit);
+
+ void *func;
+ if ((func = dlsym(handle, funcname)) == NULL) {
+ mjit_warning("skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
+ continue;
+ }
+
+ if (child_unit->iseq) { // Check whether GCed or not
+ // Usage of jit_code might be not in a critical section.
+ MJIT_ATOMIC_SET(ISEQ_BODY(child_unit->iseq)->jit_func, (jit_func_t)func);
+ }
+ }
+ }
+ verbose(1, "JIT compaction (%.1fms): Compacted %d methods %s -> %s", end_time - current_cc_ms, active_units_length, c_file, so_file);
}
#ifndef __clang__
@@ -855,81 +883,24 @@ compile_prelude(FILE *f)
#endif
}
-// Compile ISeq in UNIT and return function pointer of JIT-ed code.
-// It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong.
-static int
-mjit_compile_unit(struct rb_mjit_unit *unit)
-{
- static const char c_ext[] = ".c";
- static const char so_ext[] = DLEXT;
- char c_file[MAXPATHLEN], so_file[MAXPATHLEN], funcname[MAXPATHLEN];
-
- sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
- sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
- sprint_funcname(funcname, unit);
-
- FILE *f;
- int fd = rb_cloexec_open(c_file, c_file_access_mode, 0600);
- if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- if (fd >= 0) (void)close(fd);
- verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
- return 1;
- }
-
- // print #include of MJIT header, etc.
- compile_prelude(f);
-
- // To make MJIT worker thread-safe against GC.compact, copy ISeq values while `in_jit` is true.
- long iseq_lineno = 0;
- if (FIXNUM_P(ISEQ_BODY(unit->iseq)->location.first_lineno))
- // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG.
- iseq_lineno = FIX2LONG(ISEQ_BODY(unit->iseq)->location.first_lineno);
- char *iseq_label = alloca(RSTRING_LEN(ISEQ_BODY(unit->iseq)->location.label) + 1);
- char *iseq_path = alloca(RSTRING_LEN(rb_iseq_path(unit->iseq)) + 1);
- strcpy(iseq_label, RSTRING_PTR(ISEQ_BODY(unit->iseq)->location.label));
- strcpy(iseq_path, RSTRING_PTR(rb_iseq_path(unit->iseq)));
-
- verbose(2, "start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- fprintf(f, "/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno);
- bool success = mjit_compile(f, unit->iseq, funcname, unit->id);
-
- fclose(f);
- if (!success) {
- if (!mjit_opts.save_temps)
- remove_file(c_file);
- verbose(1, "JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
- return 1;
- }
-
- return compile_c_to_so(c_file, so_file);
-}
-
static pid_t
-start_mjit_compile(struct rb_mjit_unit *unit)
+start_c_compile_unit(struct rb_mjit_unit *unit)
{
- rb_vm_t *vm = GET_VM();
- rb_native_mutex_lock(&vm->waitpid_lock);
-
- pid_t pid = rb_fork();
+ extern pid_t rb_mjit_fork();
+ pid_t pid = rb_mjit_fork();
if (pid == 0) {
- rb_native_mutex_unlock(&vm->waitpid_lock);
-
- int exit_code = mjit_compile_unit(unit);
+ int exit_code = c_compile_unit(unit);
exit(exit_code);
}
else {
- mjit_add_waiting_pid(vm, pid);
- rb_native_mutex_unlock(&vm->waitpid_lock);
-
return pid;
}
}
-// Capture cc entries of `captured_iseq` and append them to `compiled_iseq->jit_unit->cc_entries`.
+// Capture cc entries of `captured_iseq` and append them to `compiled_iseq->mjit_unit->cc_entries`.
// This is needed when `captured_iseq` is inlined by `compiled_iseq` and GC needs to mark inlined cc.
//
-// Index to refer to `compiled_iseq->jit_unit->cc_entries` is returned instead of the address
+// Index to refer to `compiled_iseq->mjit_unit->cc_entries` is returned instead of the address
// because old addresses may be invalidated by `realloc` later. -1 is returned on failure.
//
// This assumes that it's safe to reference cc without acquiring GVL.
@@ -937,10 +908,10 @@ int
mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq)
{
VM_ASSERT(compiled_iseq != NULL);
- VM_ASSERT(compiled_iseq->jit_unit != NULL);
+ VM_ASSERT(compiled_iseq->mjit_unit != NULL);
VM_ASSERT(captured_iseq != NULL);
- struct rb_mjit_unit *unit = compiled_iseq->jit_unit;
+ struct rb_mjit_unit *unit = compiled_iseq->mjit_unit;
unsigned int new_entries_size = unit->cc_entries_size + captured_iseq->ci_size;
VM_ASSERT(captured_iseq->ci_size > 0);
@@ -969,105 +940,7 @@ mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const
return cc_entries_index;
}
-// Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
-static void
-mark_ec_units(rb_execution_context_t *ec)
-{
- const rb_control_frame_t *cfp;
-
- if (ec->vm_stack == NULL)
- return;
- for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
- const rb_iseq_t *iseq;
- if (cfp->pc && (iseq = cfp->iseq) != NULL
- && imemo_type((VALUE) iseq) == imemo_iseq
- && (ISEQ_BODY(iseq)->jit_unit) != NULL) {
- ISEQ_BODY(iseq)->jit_unit->used_code_p = true;
- }
-
- if (cfp == ec->cfp)
- break; // reached the most recent cfp
- }
-}
-
-// MJIT info related to an existing continutaion.
-struct mjit_cont {
- rb_execution_context_t *ec; // continuation ec
- struct mjit_cont *prev, *next; // used to form lists
-};
-
-// Double linked list of registered continuations. This is used to detect
-// units which are in use in unload_units.
-static struct mjit_cont *first_cont;
-
-// Unload JIT code of some units to satisfy the maximum permitted
-// number of units with a loaded code.
-static void
-unload_units(void)
-{
- struct rb_mjit_unit *unit = 0, *next;
- struct mjit_cont *cont;
- int units_num = active_units.length;
-
- // For now, we don't unload units when ISeq is GCed. We should
- // unload such ISeqs first here.
- ccan_list_for_each_safe(&active_units.head, unit, next, unode) {
- if (unit->iseq == NULL) { // ISeq is GCed.
- remove_from_list(unit, &active_units);
- free_unit(unit);
- }
- }
-
- // Detect units which are in use and can't be unloaded.
- ccan_list_for_each(&active_units.head, unit, unode) {
- VM_ASSERT(unit->iseq != NULL && unit->handle != NULL);
- unit->used_code_p = false;
- }
- // All threads have a root_fiber which has a mjit_cont. Other normal fibers also
- // have a mjit_cont. Thus we can check ISeqs in use by scanning ec of mjit_conts.
- for (cont = first_cont; cont != NULL; cont = cont->next) {
- mark_ec_units(cont->ec);
- }
- // TODO: check stale_units and unload unused ones! (note that the unit is not associated to ISeq anymore)
-
- // Unload units whose total_calls is smaller than any total_calls in unit_queue.
- // TODO: make the algorithm more efficient
- long unsigned prev_queue_calls = -1;
- while (true) {
- // Calculate the next max total_calls in unit_queue
- long unsigned max_queue_calls = 0;
- ccan_list_for_each(&unit_queue.head, unit, unode) {
- if (unit->iseq != NULL && max_queue_calls < ISEQ_BODY(unit->iseq)->total_calls
- && ISEQ_BODY(unit->iseq)->total_calls < prev_queue_calls) {
- max_queue_calls = ISEQ_BODY(unit->iseq)->total_calls;
- }
- }
- prev_queue_calls = max_queue_calls;
-
- bool unloaded_p = false;
- ccan_list_for_each_safe(&active_units.head, unit, next, unode) {
- if (unit->used_code_p) // We can't unload code on stack.
- continue;
-
- if (max_queue_calls > ISEQ_BODY(unit->iseq)->total_calls) {
- verbose(2, "Unloading unit %d (calls=%lu, threshold=%lu)",
- unit->id, ISEQ_BODY(unit->iseq)->total_calls, max_queue_calls);
- VM_ASSERT(unit->handle != NULL);
- remove_from_list(unit, &active_units);
- free_unit(unit);
- unloaded_p = true;
- }
- }
- if (!unloaded_p) break;
- }
-
- if (units_num > active_units.length) {
- verbose(1, "Too many JIT code -- %d units unloaded", units_num - active_units.length);
- total_unloads += units_num - active_units.length;
- }
-}
-
-static void mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool worker_p);
+static void mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info);
// Return an unique file name in /tmp with PREFIX and SUFFIX and
// number ID. Use getpid if ID == 0. The return file name exists
@@ -1097,6 +970,7 @@ mjit_cancel_all(const char *reason)
return;
mjit_call_p = false;
+ mjit_cancel_p = true;
if (mjit_opts.warnings || mjit_opts.verbose) {
fprintf(stderr, "JIT cancel: Disabled JIT-ed code because %s\n", reason);
}
@@ -1110,8 +984,8 @@ mjit_update_references(const rb_iseq_t *iseq)
return;
CRITICAL_SECTION_START(4, "mjit_update_references");
- if (ISEQ_BODY(iseq)->jit_unit) {
- ISEQ_BODY(iseq)->jit_unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)ISEQ_BODY(iseq)->jit_unit->iseq);
+ if (ISEQ_BODY(iseq)->mjit_unit) {
+ ISEQ_BODY(iseq)->mjit_unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)ISEQ_BODY(iseq)->mjit_unit->iseq);
// We need to invalidate JIT-ed code for the ISeq because it embeds pointer addresses.
// To efficiently do that, we use the same thing as TracePoint and thus everything is cancelled for now.
// See mjit.h and tool/ruby_vm/views/_mjit_compile_insn.erb for how `mjit_call_p` is used.
@@ -1119,7 +993,7 @@ mjit_update_references(const rb_iseq_t *iseq)
}
// Units in stale_units (list of over-speculated and invalidated code) are not referenced from
- // `ISEQ_BODY(iseq)->jit_unit` anymore (because new one replaces that). So we need to check them too.
+ // `ISEQ_BODY(iseq)->mjit_unit` anymore (because new one replaces that). So we need to check them too.
// TODO: we should be able to reduce the number of units checked here.
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&stale_units.head, unit, unode) {
@@ -1138,13 +1012,13 @@ mjit_free_iseq(const rb_iseq_t *iseq)
if (!mjit_enabled)
return;
- if (ISEQ_BODY(iseq)->jit_unit) {
- // jit_unit is not freed here because it may be referred by multiple
+ if (ISEQ_BODY(iseq)->mjit_unit) {
+ // mjit_unit is not freed here because it may be referred by multiple
// lists of units. `get_from_list` and `mjit_finish` do the job.
- ISEQ_BODY(iseq)->jit_unit->iseq = NULL;
+ ISEQ_BODY(iseq)->mjit_unit->iseq = NULL;
}
// Units in stale_units (list of over-speculated and invalidated code) are not referenced from
- // `ISEQ_BODY(iseq)->jit_unit` anymore (because new one replaces that). So we need to check them too.
+ // `ISEQ_BODY(iseq)->mjit_unit` anymore (because new one replaces that). So we need to check them too.
// TODO: we should be able to reduce the number of units checked here.
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&stale_units.head, unit, unode) {
@@ -1174,7 +1048,7 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
if (unit->handle && dlclose(unit->handle)) {
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
}
- free(unit);
+ xfree(unit);
}
else {
free_unit(unit);
@@ -1183,129 +1057,73 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
list->length = 0;
}
-// Register a new continuation with execution context `ec`. Return MJIT info about
-// the continuation.
-struct mjit_cont *
-mjit_cont_new(rb_execution_context_t *ec)
-{
- struct mjit_cont *cont;
-
- // We need to use calloc instead of something like ZALLOC to avoid triggering GC here.
- // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup,
- // the thread is still being prepared and marking it causes SEGV.
- cont = calloc(1, sizeof(struct mjit_cont));
- if (cont == NULL)
- rb_memerror();
- cont->ec = ec;
-
- CRITICAL_SECTION_START(3, "in mjit_cont_new");
- if (first_cont == NULL) {
- cont->next = cont->prev = NULL;
- }
- else {
- cont->prev = NULL;
- cont->next = first_cont;
- first_cont->prev = cont;
- }
- first_cont = cont;
- CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
-
- return cont;
-}
-
-// Unregister continuation `cont`.
-void
-mjit_cont_free(struct mjit_cont *cont)
+static struct rb_mjit_unit*
+create_unit(enum rb_mjit_unit_type type)
{
- CRITICAL_SECTION_START(3, "in mjit_cont_new");
- if (cont == first_cont) {
- first_cont = cont->next;
- if (first_cont != NULL)
- first_cont->prev = NULL;
- }
- else {
- cont->prev->next = cont->next;
- if (cont->next != NULL)
- cont->next->prev = cont->prev;
+ struct rb_mjit_unit *unit = ZALLOC_N(struct rb_mjit_unit, 1);
+ unit->id = current_unit_num++;
+ unit->type = type;
+ if (type == MJIT_UNIT_BATCH) {
+ ccan_list_head_init(&unit->units.head);
}
- CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
-
- free(cont);
+ return unit;
}
-// Finish work with continuation info.
-static void
-finish_conts(void)
+static struct rb_mjit_unit*
+create_iseq_unit(const rb_iseq_t *iseq)
{
- struct mjit_cont *cont, *next;
-
- for (cont = first_cont; cont != NULL; cont = next) {
- next = cont->next;
- xfree(cont);
- }
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_ISEQ);
+ unit->iseq = (rb_iseq_t *)iseq;
+ ISEQ_BODY(iseq)->mjit_unit = unit;
+ return unit;
}
-static void mjit_wait(struct rb_iseq_constant_body *body);
+static void mjit_wait(struct rb_mjit_unit *unit);
// Check the unit queue and start mjit_compile if nothing is in progress.
static void
check_unit_queue(void)
{
+ if (mjit_opts.custom) return; // Custom RubyVM::MJIT.compile is in use
if (worker_stopped) return;
if (current_cc_pid != 0) return; // still compiling
- // Run unload_units after it's requested `max_cache_size / 10` (default: 10) times.
- // This throttles the call to mitigate locking in unload_units. It also throttles JIT compaction.
- int throttle_threshold = mjit_opts.max_cache_size / 10;
- if (unload_requests >= throttle_threshold) {
- unload_units();
- unload_requests = 0;
- if (active_units.length == mjit_opts.max_cache_size && mjit_opts.wait) { // Sometimes all methods may be in use
- mjit_opts.max_cache_size++; // avoid infinite loop on `rb_mjit_wait_call`. Note that --jit-wait is just for testing.
- verbose(1, "No units can be unloaded -- incremented max-cache-size to %d for --jit-wait", mjit_opts.max_cache_size);
- }
- }
- if (active_units.length >= mjit_opts.max_cache_size) return; // wait until unload_units makes a progress
+ // TODO: resurrect unload_units
+ if (active_units_length >= mjit_opts.max_cache_size) return; // wait until unload_units makes a progress
- // Dequeue a unit
- struct rb_mjit_unit *unit = get_from_list(&unit_queue);
- if (unit == NULL) return;
+ // No ISEQ in unit_queue has enough calls to trigger JIT
+ if (!mjit_compile_p) return;
+ mjit_compile_p = false;
+ // Compile all ISEQs in unit_queue together
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_BATCH);
+ struct rb_mjit_unit *child_unit = NULL;
+ VM_ASSERT(unit_queue.length > 0);
+ while ((child_unit = get_from_list(&unit_queue)) != NULL && (active_units_length + unit->units.length) < mjit_opts.max_cache_size) {
+ add_to_list(child_unit, &unit->units);
+ ISEQ_BODY(child_unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_COMPILING;
+ }
+
+ // Run the MJIT compiler synchronously
current_cc_ms = real_ms_time();
current_cc_unit = unit;
- current_cc_pid = start_mjit_compile(unit);
-
- // JIT failure
- if (current_cc_pid == -1) {
- current_cc_pid = 0;
- current_cc_unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // TODO: consider unit->compact_p
- current_cc_unit = NULL;
+ bool success = mjit_batch(unit);
+ if (!success) {
+ mjit_notify_waitpid(1);
return;
}
+ // Run the C compiler asynchronously (unless --mjit-wait)
if (mjit_opts.wait) {
- mjit_wait(unit->iseq->body);
+ int exit_code = c_compile_unit(unit);
+ mjit_notify_waitpid(exit_code);
}
-}
-
-// Create unit for `iseq`. This function may be called from an MJIT worker.
-static struct rb_mjit_unit*
-create_unit(const rb_iseq_t *iseq)
-{
- // To prevent GC, don't use ZALLOC // TODO: just use ZALLOC
- struct rb_mjit_unit *unit = calloc(1, sizeof(struct rb_mjit_unit));
- if (unit == NULL)
- return NULL;
-
- unit->id = current_unit_num++;
- if (iseq == NULL) { // Compact unit
- unit->compact_p = true;
- }
- else { // Normal unit
- unit->iseq = (rb_iseq_t *)iseq;
- ISEQ_BODY(iseq)->jit_unit = unit;
+ else {
+ current_cc_pid = start_c_compile_unit(unit);
+ if (current_cc_pid == -1) { // JIT failure
+ mjit_notify_waitpid(1);
+ }
}
- return unit;
}
// Check if it should compact all JIT code and start it as needed
@@ -1317,29 +1135,43 @@ check_compaction(void)
int max_compact_size = mjit_opts.max_cache_size / 100;
if (max_compact_size < 10) max_compact_size = 10;
- // Run unload_units after it's requested `max_cache_size / 10` (default: 10) times.
- // This throttles the call to mitigate locking in unload_units. It also throttles JIT compaction.
- int throttle_threshold = mjit_opts.max_cache_size / 10;
+ // Run JIT compaction only when it's going to add 10%+ units.
+ int throttle_threshold = active_units_length / 10;
if (compact_units.length < max_compact_size
+ && active_units_length - compact_units_length > throttle_threshold
&& ((!mjit_opts.wait && unit_queue.length == 0 && active_units.length > 1)
- || (active_units.length == mjit_opts.max_cache_size && compact_units.length * throttle_threshold <= total_unloads))) { // throttle compaction by total_unloads
- struct rb_mjit_unit *unit = create_unit(NULL);
- if (unit != NULL) {
- // TODO: assert unit is null
- current_cc_ms = real_ms_time();
- current_cc_unit = unit;
- current_cc_pid = start_mjit_compact(unit);
- // TODO: check -1
+ || (active_units_length == mjit_opts.max_cache_size))) {
+ struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_COMPACT);
+
+ // Run the MJIT compiler synchronously
+ current_cc_ms = real_ms_time();
+ current_cc_unit = unit;
+ bool success = mjit_compact(unit);
+ if (!success) {
+ mjit_notify_waitpid(1);
+ return;
+ }
+
+ // Run the C compiler asynchronously (unless --mjit-wait)
+ if (mjit_opts.wait) {
+ int exit_code = c_compile_unit(unit);
+ mjit_notify_waitpid(exit_code);
+ }
+ else {
+ current_cc_pid = start_c_compile_unit(unit);
+ if (current_cc_pid == -1) { // JIT failure
+ mjit_notify_waitpid(1);
+ }
}
}
}
// Check the current CC process if any, and start a next C compiler process as needed.
void
-mjit_notify_waitpid(int status)
+mjit_notify_waitpid(int exit_code)
{
- // TODO: check current_cc_pid?
+ VM_ASSERT(mjit_opts.wait || current_cc_pid != 0);
current_cc_pid = 0;
// Delete .c file
@@ -1347,15 +1179,10 @@ mjit_notify_waitpid(int status)
sprint_uniq_filename(c_file, (int)sizeof(c_file), current_cc_unit->id, MJIT_TMP_PREFIX, ".c");
// Check the result
- bool success = false;
- if (WIFEXITED(status)) {
- success = (WEXITSTATUS(status) == 0);
- }
- if (!success) {
+ if (exit_code != 0) {
verbose(2, "Failed to generate so");
- if (!current_cc_unit->compact_p) {
- current_cc_unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
- }
+ // TODO: set MJIT_FUNC_FAILED to unit->units
+ // TODO: free list of unit->units
free_unit(current_cc_unit);
current_cc_unit = NULL;
return;
@@ -1364,39 +1191,22 @@ mjit_notify_waitpid(int status)
// Load .so file
char so_file[MAXPATHLEN];
sprint_uniq_filename(so_file, (int)sizeof(so_file), current_cc_unit->id, MJIT_TMP_PREFIX, DLEXT);
- if (current_cc_unit->compact_p) { // Compact unit
- load_compact_funcs_from_so(current_cc_unit, c_file, so_file);
- current_cc_unit = NULL;
- }
- else { // Normal unit
- // Load the function from so
- char funcname[MAXPATHLEN];
- sprint_funcname(funcname, current_cc_unit);
- void *func = load_func_from_so(so_file, funcname, current_cc_unit);
-
- // Delete .so file
- if (!mjit_opts.save_temps)
- remove_file(so_file);
-
- // Set the jit_func if successful
- if (current_cc_unit->iseq != NULL) { // mjit_free_iseq could nullify this
- rb_iseq_t *iseq = current_cc_unit->iseq;
- if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- double end_time = real_ms_time();
- verbose(1, "JIT success (%.1fms): %s@%s:%ld -> %s",
- end_time - current_cc_ms, RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2LONG(ISEQ_BODY(iseq)->location.first_lineno), c_file);
-
- add_to_list(current_cc_unit, &active_units);
- }
- MJIT_ATOMIC_SET(ISEQ_BODY(iseq)->jit_func, func);
- } // TODO: free unit on else?
+ switch (current_cc_unit->type) {
+ case MJIT_UNIT_ISEQ:
+ rb_bug("unreachable: current_cc_unit->type must not be MJIT_UNIT_ISEQ");
+ case MJIT_UNIT_BATCH:
+ load_batch_funcs_from_so(current_cc_unit, c_file, so_file);
current_cc_unit = NULL;
// Run compaction if it should
if (!stop_worker_p) {
check_compaction();
}
+ break;
+ case MJIT_UNIT_COMPACT:
+ load_compact_funcs_from_so(current_cc_unit, c_file, so_file);
+ current_cc_unit = NULL;
+ break;
}
// Skip further compilation if mjit_finish is trying to stop it
@@ -1413,33 +1223,154 @@ mjit_target_iseq_p(const rb_iseq_t *iseq)
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
&& !body->builtin_inline_p
- && strcmp("<internal:mjit>", RSTRING_PTR(rb_iseq_path(iseq)));
+ && strcmp("<internal:mjit>", RSTRING_PTR(rb_iseq_path(iseq))) != 0;
+}
+
+// RubyVM::MJIT
+static VALUE rb_mMJIT = 0;
+// RubyVM::MJIT::C
+static VALUE rb_mMJITC = 0;
+// RubyVM::MJIT::Compiler
+static VALUE rb_cMJITCompiler = 0;
+// RubyVM::MJIT::CPointer::Struct_rb_iseq_t
+static VALUE rb_cMJITIseqPtr = 0;
+// RubyVM::MJIT::CPointer::Struct_IC
+static VALUE rb_cMJITICPtr = 0;
+// RubyVM::MJIT::Compiler
+static VALUE rb_mMJITHooks = 0;
+
+#define WITH_MJIT_DISABLED(stmt) do { \
+ bool original_call_p = mjit_call_p; \
+ mjit_call_p = false; \
+ stmt; \
+ mjit_call_p = original_call_p; \
+ if (mjit_cancel_p) mjit_call_p = false; \
+} while (0);
+
+// Hook MJIT when BOP is redefined.
+MJIT_FUNC_EXPORTED void
+rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_bop_redefined"), 2, INT2NUM(redefined_flag), INT2NUM((int)bop));
+ });
+}
+
+// Hook MJIT when CME is invalidated.
+MJIT_FUNC_EXPORTED void
+rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ VALUE cme_klass = rb_funcall(rb_mMJITC, rb_intern("rb_callable_method_entry_struct"), 0);
+ VALUE cme_ptr = rb_funcall(cme_klass, rb_intern("new"), 1, SIZET2NUM((size_t)cme));
+ rb_funcall(rb_mMJITHooks, rb_intern("on_cme_invalidate"), 1, cme_ptr);
+ });
+}
+
+// Hook MJIT when Ractor is spawned.
+void
+rb_mjit_before_ractor_spawn(void)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_ractor_spawn"), 0);
+ });
}
-// If recompile_p is true, the call is initiated by mjit_recompile.
-// This assumes the caller holds CRITICAL_SECTION when recompile_p is true.
static void
-mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool recompile_p)
+mjit_constant_state_changed(void *data)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ ID id = (ID)data;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_constant_state_changed"), 1, ID2SYM(id));
+ });
+}
+
+// Hook MJIT when constant state is changed.
+MJIT_FUNC_EXPORTED void
+rb_mjit_constant_state_changed(ID id)
{
- // TODO: Support non-main Ractors
- if (!mjit_enabled || pch_status == PCH_FAILED || !rb_ractor_main_p())
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ // Asynchronously hook the Ruby code since this is hooked during a "Ruby critical section".
+ extern int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data);
+ rb_workqueue_register(0, mjit_constant_state_changed, (void *)id);
+}
+
+// Hook MJIT when constant IC is updated.
+MJIT_FUNC_EXPORTED void
+rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
+ VALUE ic_ptr = rb_funcall(rb_cMJITICPtr, rb_intern("new"), 1, SIZET2NUM((size_t)ic));
+ rb_funcall(rb_mMJITHooks, rb_intern("on_constant_ic_update"), 3, iseq_ptr, ic_ptr, UINT2NUM(insn_idx));
+ });
+}
+
+// Hook MJIT when TracePoint is enabled.
+MJIT_FUNC_EXPORTED void
+rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events)
+{
+ if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks) return;
+ WITH_MJIT_DISABLED({
+ rb_funcall(rb_mMJITHooks, rb_intern("on_tracing_invalidate_all"), 1, UINT2NUM(new_iseq_events));
+ });
+}
+
+// [experimental] Call custom RubyVM::MJIT.compile if defined
+static void
+mjit_hook_custom_compile(const rb_iseq_t *iseq)
+{
+ WITH_MJIT_DISABLED({
+ VALUE iseq_class = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0);
+ VALUE iseq_ptr = rb_funcall(iseq_class, rb_intern("new"), 1, ULONG2NUM((size_t)iseq));
+ VALUE jit_func = rb_funcall(rb_mMJIT, rb_intern("compile"), 1, iseq_ptr);
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)NUM2ULONG(jit_func);
+ });
+}
+
+static void
+mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info)
+{
+ if (!mjit_enabled) return;
+ if (mjit_opts.custom) { // Hook custom RubyVM::MJIT.compile if defined
+ mjit_hook_custom_compile(iseq);
+ return;
+ }
+ if (pch_status != PCH_SUCCESS || !rb_ractor_main_p()) // TODO: Support non-main Ractors
return;
if (!mjit_target_iseq_p(iseq)) {
- ISEQ_BODY(iseq)->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // skip mjit_wait
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED; // skip mjit_wait
return;
}
- RB_DEBUG_COUNTER_INC(mjit_add_iseq_to_process);
- ISEQ_BODY(iseq)->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC;
- create_unit(iseq);
- if (ISEQ_BODY(iseq)->jit_unit == NULL)
- // Failure in creating the unit.
- return;
- if (compile_info != NULL)
- ISEQ_BODY(iseq)->jit_unit->compile_info = *compile_info;
- add_to_list(ISEQ_BODY(iseq)->jit_unit, &unit_queue);
- if (active_units.length >= mjit_opts.max_cache_size) {
- unload_requests++;
+ // For batching multiple ISEQs, we only enqueue ISEQs when total_calls reaches call_threshold,
+ // and compile all enqueued ISEQs when any ISEQ reaches call_threshold * 2.
+ bool recompile_p = !MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func);
+ if (!ISEQ_BODY(iseq)->mjit_unit || recompile_p) { // call_threshold, or recompile
+ // Discard an old unit with recompile_p
+ if (recompile_p) {
+ ISEQ_BODY(iseq)->mjit_unit->iseq = NULL; // Ignore this from compaction
+ ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_NOT_COMPILED;
+ active_units_length--;
+ }
+
+ // Create a new unit and enqueue it
+ struct rb_mjit_unit *unit = create_iseq_unit(iseq);
+ if (recompile_p) {
+ VM_ASSERT(compile_info != NULL);
+ unit->compile_info = *compile_info;
+ }
+ add_to_list(unit, &unit_queue);
+ ISEQ_BODY(iseq)->total_calls = 0; // come here again :)
+ }
+ else { // call_threshold * 2
+ VM_ASSERT(compile_info == NULL);
+ mjit_compile_p = true; // compile all ISEQs in unit_queue
}
}
@@ -1448,26 +1379,30 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
void
rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
{
- mjit_add_iseq_to_process(iseq, NULL, false);
+ mjit_add_iseq_to_process(iseq, NULL);
check_unit_queue();
}
-// For this timeout seconds, --jit-wait will wait for JIT compilation finish.
-#define MJIT_WAIT_TIMEOUT_SECONDS 60
+// For this timeout seconds, mjit_finish will wait for JIT compilation finish.
+#define MJIT_WAIT_TIMEOUT_SECONDS 5
static void
-mjit_wait(struct rb_iseq_constant_body *body)
+mjit_wait(struct rb_mjit_unit *unit)
{
pid_t initial_pid = current_cc_pid;
- struct timeval tv;
+ if (initial_pid == 0) {
+ mjit_warning("initial_pid was 0 on mjit_wait");
+ return;
+ }
+ if (pch_status == PCH_FAILED) return;
+
int tries = 0;
- tv.tv_sec = 0;
- tv.tv_usec = 1000;
- while (body == NULL ? current_cc_pid == initial_pid : body->jit_func == (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC) { // TODO: refactor this
+ struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 };
+ while (current_cc_pid == initial_pid) {
tries++;
- if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
- if (body != NULL) {
- body->jit_func = (mjit_func_t) NOT_COMPILED_JIT_ISEQ_FUNC; // JIT worker seems dead. Give up.
+ if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS) {
+ if (unit->type == MJIT_UNIT_ISEQ) {
+ unit->iseq->body->jit_func = (jit_func_t)MJIT_FUNC_FAILED; // C compiler was too slow. Give up.
}
mjit_warning("timed out to wait for JIT finish");
break;
@@ -1477,50 +1412,24 @@ mjit_wait(struct rb_iseq_constant_body *body)
}
}
-static void
-mjit_wait_unit(struct rb_mjit_unit *unit)
-{
- if (unit->compact_p) {
- mjit_wait(NULL);
- }
- else {
- mjit_wait(current_cc_unit->iseq->body);
- }
-}
-
-// Wait for JIT compilation finish for --jit-wait, and call the function pointer
-// if the compiled result is not NOT_COMPILED_JIT_ISEQ_FUNC.
-VALUE
-rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
-{
- if (worker_stopped)
- return Qundef;
-
- mjit_wait(body);
- if ((uintptr_t)body->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC) {
- return Qundef;
- }
- return body->jit_func(ec, ec->cfp);
-}
-
struct rb_mjit_compile_info*
rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body)
{
- VM_ASSERT(body->jit_unit != NULL);
- return &body->jit_unit->compile_info;
+ VM_ASSERT(body->mjit_unit != NULL);
+ return &body->mjit_unit->compile_info;
}
static void
mjit_recompile(const rb_iseq_t *iseq)
{
- if ((uintptr_t)ISEQ_BODY(iseq)->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC)
+ if (MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func))
return;
verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- VM_ASSERT(ISEQ_BODY(iseq)->jit_unit != NULL);
+ RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
+ VM_ASSERT(ISEQ_BODY(iseq)->mjit_unit != NULL);
- mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->jit_unit->compile_info, true);
+ mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->mjit_unit->compile_info);
check_unit_queue();
}
@@ -1705,9 +1614,9 @@ system_tmpdir(void)
// Minimum value for JIT cache size.
#define MIN_CACHE_SIZE 10
// Default permitted number of units with a JIT code kept in memory.
-#define DEFAULT_MAX_CACHE_SIZE 10000
+#define DEFAULT_MAX_CACHE_SIZE 100
// A default threshold used to add iseq to JIT.
-#define DEFAULT_MIN_CALLS_TO_ADD 10000
+#define DEFAULT_CALL_THRESHOLD 10000
// Start MJIT worker. Return TRUE if worker is successfully started.
static bool
@@ -1768,19 +1677,19 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
return;
}
else if (opt_match_noarg(s, l, "warnings")) {
- mjit_opt->warnings = 1;
+ mjit_opt->warnings = true;
}
else if (opt_match(s, l, "debug")) {
if (*s)
mjit_opt->debug_flags = strdup(s + 1);
else
- mjit_opt->debug = 1;
+ mjit_opt->debug = true;
}
else if (opt_match_noarg(s, l, "wait")) {
- mjit_opt->wait = 1;
+ mjit_opt->wait = true;
}
else if (opt_match_noarg(s, l, "save-temps")) {
- mjit_opt->save_temps = 1;
+ mjit_opt->save_temps = true;
}
else if (opt_match(s, l, "verbose")) {
mjit_opt->verbose = *s ? atoi(s + 1) : 1;
@@ -1788,8 +1697,12 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
else if (opt_match_arg(s, l, "max-cache")) {
mjit_opt->max_cache_size = atoi(s + 1);
}
- else if (opt_match_arg(s, l, "min-calls")) {
- mjit_opt->min_calls = atoi(s + 1);
+ else if (opt_match_arg(s, l, "call-threshold")) {
+ mjit_opt->call_threshold = atoi(s + 1);
+ }
+ // --mjit=pause is an undocumented feature for experiments
+ else if (opt_match_noarg(s, l, "pause")) {
+ mjit_opt->pause = true;
}
else {
rb_raise(rb_eRuntimeError,
@@ -1799,15 +1712,15 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt)
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
const struct ruby_opt_message mjit_option_messages[] = {
- M("--mjit-warnings", "", "Enable printing JIT warnings"),
- M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"),
- M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"),
- M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"),
- M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"),
- M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: "
+ M("--mjit-warnings", "", "Enable printing JIT warnings"),
+ M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"),
+ M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"),
+ M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"),
+ M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"),
+ M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: "
STRINGIZE(DEFAULT_MAX_CACHE_SIZE) ")"),
- M("--mjit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: "
- STRINGIZE(DEFAULT_MIN_CALLS_TO_ADD) ")"),
+ M("--mjit-call-threshold=num", "", "Number of calls to trigger JIT (for testing, default: "
+ STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
{0}
};
#undef M
@@ -1818,14 +1731,34 @@ const struct ruby_opt_message mjit_option_messages[] = {
void
mjit_init(const struct mjit_options *opts)
{
+ VM_ASSERT(mjit_enabled);
mjit_opts = *opts;
- mjit_enabled = true;
+
+ // MJIT doesn't support miniruby, but it might reach here by MJIT_FORCE_ENABLE.
+ rb_mMJIT = rb_const_get(rb_cRubyVM, rb_intern("MJIT"));
+ if (!rb_const_defined(rb_mMJIT, rb_intern("Compiler"))) {
+ verbose(1, "Disabling MJIT because RubyVM::MJIT::Compiler is not defined");
+ mjit_enabled = false;
+ return;
+ }
+ rb_mMJITC = rb_const_get(rb_mMJIT, rb_intern("C"));
+ rb_cMJITCompiler = rb_funcall(rb_const_get(rb_mMJIT, rb_intern("Compiler")), rb_intern("new"), 0);
+ rb_cMJITIseqPtr = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0);
+ rb_cMJITICPtr = rb_funcall(rb_mMJITC, rb_intern("IC"), 0);
+ rb_funcall(rb_cMJITICPtr, rb_intern("new"), 1, SIZET2NUM(0)); // Trigger no-op constant events before enabling hooks
+ rb_mMJITHooks = rb_const_get(rb_mMJIT, rb_intern("Hooks"));
+
mjit_call_p = true;
mjit_pid = getpid();
// Normalize options
- if (mjit_opts.min_calls == 0)
- mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
+ if (mjit_opts.call_threshold == 0)
+ mjit_opts.call_threshold = DEFAULT_CALL_THRESHOLD;
+ if (mjit_opts.call_threshold % 2 == 1) {
+ mjit_opts.call_threshold += 1;
+ mjit_warning("--mjit-call-threshold must be an even number. Using %d instead.", mjit_opts.call_threshold);
+ }
+ mjit_opts.call_threshold /= 2; // Half for enqueue, half for trigger
if (mjit_opts.max_cache_size <= 0)
mjit_opts.max_cache_size = DEFAULT_MAX_CACHE_SIZE;
if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
@@ -1840,10 +1773,10 @@ mjit_init(const struct mjit_options *opts)
cc_added_args = split_flags(opts->debug_flags);
xfree(opts->debug_flags);
#if MJIT_CFLAGS_PIPE
- // eliminate a flag incompatible with `-pipe`
+ // Filter out `-save-temps`. It's a C compiler flag used by update-deps and not compatible with `-pipe`.
for (size_t i = 0, j = 0; i < sizeof(CC_COMMON_ARGS) / sizeof(char *); i++) {
if (CC_COMMON_ARGS[i] && strncmp("-save-temps", CC_COMMON_ARGS[i], strlen("-save-temps")) == 0)
- continue; // skip -save-temps flag
+ continue; // Skip `-save-temps`
cc_common_args[j] = CC_COMMON_ARGS[i];
j++;
}
@@ -1861,23 +1794,16 @@ mjit_init(const struct mjit_options *opts)
// Initialize mutex
rb_native_mutex_initialize(&mjit_engine_mutex);
- rb_native_cond_initialize(&mjit_pch_wakeup);
- rb_native_cond_initialize(&mjit_client_wakeup);
- rb_native_cond_initialize(&mjit_worker_wakeup);
- rb_native_cond_initialize(&mjit_gc_wakeup);
-
- // Make sure the saved_ec of the initial thread's root_fiber is scanned by mark_ec_units.
- //
- // rb_threadptr_root_fiber_setup for the initial thread is called before mjit_init,
- // meaning mjit_cont_new is skipped for the root_fiber. Therefore we need to call
- // rb_fiber_init_mjit_cont again with mjit_enabled=true to set the root_fiber's mjit_cont.
- rb_fiber_init_mjit_cont(GET_EC()->fiber_ptr);
-
- // Initialize worker thread
- start_worker();
- // TODO: Consider running C compiler asynchronously
- make_pch();
+ // If --mjit=pause is given, lazily start MJIT when RubyVM::MJIT.resume is called.
+ // You can use it to control MJIT warmup, or to customize the JIT implementation.
+ if (!mjit_opts.pause) {
+ // TODO: Consider running C compiler asynchronously
+ make_pch();
+
+ // Enable MJIT compilation
+ start_worker();
+ }
}
static void
@@ -1885,7 +1811,7 @@ stop_worker(void)
{
stop_worker_p = true;
if (current_cc_unit != NULL) {
- mjit_wait_unit(current_cc_unit);
+ mjit_wait(current_cc_unit);
}
worker_stopped = true;
}
@@ -1904,7 +1830,7 @@ mjit_pause(bool wait_p)
// Flush all queued units with no option or `wait: true`
if (wait_p) {
while (current_cc_unit != NULL) {
- mjit_wait_unit(current_cc_unit);
+ mjit_wait(current_cc_unit);
}
}
@@ -1923,6 +1849,19 @@ mjit_resume(void)
return Qfalse;
}
+ // Lazily prepare PCH when --mjit=pause is given
+ if (pch_status == PCH_NOT_READY) {
+ if (rb_respond_to(rb_mMJIT, rb_intern("compile"))) {
+ // [experimental] defining RubyVM::MJIT.compile allows you to replace JIT
+ mjit_opts.custom = true;
+ pch_status = PCH_SUCCESS;
+ }
+ else {
+ // Lazy MJIT boot
+ make_pch();
+ }
+ }
+
if (!start_worker()) {
rb_raise(rb_eRuntimeError, "Failed to resume MJIT worker");
}
@@ -1954,22 +1893,6 @@ mjit_child_after_fork(void)
start_worker();
}
-// Edit 0 to 1 to enable this feature for investigating hot methods
-#define MJIT_COUNTER 0
-#if MJIT_COUNTER
-static void
-mjit_dump_total_calls(void)
-{
- struct rb_mjit_unit *unit;
- fprintf(stderr, "[MJIT_COUNTER] total_calls of active_units:\n");
- ccan_list_for_each(&active_units.head, unit, unode) {
- const rb_iseq_t *iseq = unit->iseq;
- fprintf(stderr, "%8ld: %s@%s:%d\n", ISEQ_BODY(iseq)->total_calls, RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- }
-}
-#endif
-
// Finish the threads processing units and creating PCH, finalize
// and free MJIT data. It should be called last during MJIT
// life.
@@ -1987,16 +1910,8 @@ mjit_finish(bool close_handle_p)
stop_worker();
rb_native_mutex_destroy(&mjit_engine_mutex);
- rb_native_cond_destroy(&mjit_pch_wakeup);
- rb_native_cond_destroy(&mjit_client_wakeup);
- rb_native_cond_destroy(&mjit_worker_wakeup);
- rb_native_cond_destroy(&mjit_gc_wakeup);
-
-#if MJIT_COUNTER
- mjit_dump_total_calls();
-#endif
- if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
+ if (!mjit_opts.save_temps && getpid() == pch_owner_pid && pch_status == PCH_SUCCESS && !mjit_opts.custom)
remove_file(pch_file);
xfree(header_file); header_file = NULL;
@@ -2012,7 +1927,6 @@ mjit_finish(bool close_handle_p)
free_list(&active_units, close_handle_p);
free_list(&compact_units, close_handle_p);
free_list(&stale_units, close_handle_p);
- finish_conts();
mjit_enabled = false;
verbose(1, "Successful MJIT finish");
@@ -2029,6 +1943,13 @@ mjit_mark(void)
return;
RUBY_MARK_ENTER("mjit");
+ // Mark objects used by the MJIT compiler
+ rb_gc_mark(rb_cMJITCompiler);
+ rb_gc_mark(rb_cMJITIseqPtr);
+ rb_gc_mark(rb_cMJITICPtr);
+ rb_gc_mark(rb_mMJITHooks);
+
+ // Mark JIT-compiled ISEQs
struct rb_mjit_unit *unit = NULL;
ccan_list_for_each(&active_units.head, unit, unode) {
rb_gc_mark((VALUE)unit->iseq);
@@ -2042,9 +1963,9 @@ void
mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body)
{
const struct rb_callcache **cc_entries;
- if (body->jit_unit && (cc_entries = body->jit_unit->cc_entries) != NULL) {
- // It must be `body->jit_unit->cc_entries_size` instead of `body->ci_size` to mark children's cc_entries
- for (unsigned int i = 0; i < body->jit_unit->cc_entries_size; i++) {
+ if (body->mjit_unit && (cc_entries = body->mjit_unit->cc_entries) != NULL) {
+ // It must be `body->mjit_unit->cc_entries_size` instead of `body->ci_size` to mark children's cc_entries
+ for (unsigned int i = 0; i < body->mjit_unit->cc_entries_size; i++) {
const struct rb_callcache *cc = cc_entries[i];
if (cc != NULL && vm_cc_markable(cc)) {
// Pin `cc` and `cc->cme` against GC.compact as their addresses may be written in JIT-ed code.
@@ -2055,6 +1976,24 @@ mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body)
}
}
+// Compile ISeq to C code in `f`. It returns true if it succeeds to compile.
+bool
+mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
+{
+ bool original_call_p = mjit_call_p;
+ mjit_call_p = false; // Avoid impacting JIT metrics by itself
+
+ VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern("new"), 1, ULONG2NUM((size_t)iseq));
+ VALUE src = rb_funcall(rb_cMJITCompiler, rb_intern("compile"), 3,
+ iseq_ptr, rb_str_new_cstr(funcname), INT2NUM(id));
+ if (!NIL_P(src)) {
+ fprintf(f, "%s", RSTRING_PTR(src));
+ }
+
+ mjit_call_p = original_call_p;
+ return !NIL_P(src);
+}
+
#include "mjit.rbinc"
#endif // USE_MJIT
diff --git a/mjit.h b/mjit.h
index 045612d7be..260cf4af78 100644
--- a/mjit.h
+++ b/mjit.h
@@ -2,9 +2,10 @@
#define RUBY_MJIT_H 1
/**********************************************************************
- mjit.h - Interface to MRI method JIT compiler for Ruby's main thread
+ mjit.h - Interface to MRI method JIT compiler
Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
@@ -14,51 +15,54 @@
# if USE_MJIT
-#include "debug_counter.h"
#include "ruby.h"
#include "vm_core.h"
// Special address values of a function generated from the
// corresponding iseq by MJIT:
-enum rb_mjit_iseq_func {
- // ISEQ has never been enqueued to unit_queue yet
- NOT_ADDED_JIT_ISEQ_FUNC = 0,
+enum rb_mjit_func_state {
+ // ISEQ has not been compiled yet
+ MJIT_FUNC_NOT_COMPILED = 0,
// ISEQ is already queued for the machine code generation but the
// code is not ready yet for the execution
- NOT_READY_JIT_ISEQ_FUNC = 1,
+ MJIT_FUNC_COMPILING = 1,
// ISEQ included not compilable insn, some internal assertion failed
// or the unit is unloaded
- NOT_COMPILED_JIT_ISEQ_FUNC = 2,
- // End mark
- LAST_JIT_ISEQ_FUNC = 3
+ MJIT_FUNC_FAILED = 2,
};
+// Return true if jit_func is part of enum rb_mjit_func_state
+#define MJIT_FUNC_STATE_P(jit_func) ((uintptr_t)(jit_func) <= (uintptr_t)MJIT_FUNC_FAILED)
// MJIT options which can be defined on the MRI command line.
struct mjit_options {
// Converted from "jit" feature flag to tell the enablement
// information to ruby_show_version().
- char on;
+ bool on;
// Save temporary files after MRI finish. The temporary files
// include the pre-compiled header, C code file generated for ISEQ,
// and the corresponding object file.
- char save_temps;
+ bool save_temps;
// Print MJIT warnings to stderr.
- char warnings;
+ bool warnings;
// Disable compiler optimization and add debug symbols. It can be
// very slow.
- char debug;
+ bool debug;
// Add arbitrary cflags.
char* debug_flags;
- // If not 0, all ISeqs are synchronously compiled. For testing.
- unsigned int wait;
+ // If true, all ISeqs are synchronously compiled. For testing.
+ bool wait;
// Number of calls to trigger JIT compilation. For testing.
- unsigned int min_calls;
+ unsigned int call_threshold;
// Force printing info about MJIT work of level VERBOSE or
// less. 0=silence, 1=medium, 2=verbose.
int verbose;
// Maximal permitted number of iseq JIT codes in a MJIT memory
// cache.
int max_cache_size;
+ // [experimental] Do not start MJIT until MJIT.resume is called.
+ bool pause;
+ // [experimental] Call custom RubyVM::MJIT.compile instead of MJIT.
+ bool custom;
};
// State of optimization switches
@@ -75,14 +79,13 @@ struct rb_mjit_compile_info {
bool disable_const_cache;
};
-typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
+typedef VALUE (*jit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
RUBY_SYMBOL_EXPORT_BEGIN
RUBY_EXTERN struct mjit_options mjit_opts;
RUBY_EXTERN bool mjit_call_p;
extern void rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq);
-extern VALUE rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body);
extern struct rb_mjit_compile_info* rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body);
extern void rb_mjit_recompile_send(const rb_iseq_t *iseq);
extern void rb_mjit_recompile_ivar(const rb_iseq_t *iseq);
@@ -97,10 +100,15 @@ extern void mjit_init(const struct mjit_options *opts);
extern void mjit_free_iseq(const rb_iseq_t *iseq);
extern void mjit_update_references(const rb_iseq_t *iseq);
extern void mjit_mark(void);
-extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec);
-extern void mjit_cont_free(struct mjit_cont *cont);
extern void mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body);
-extern void mjit_notify_waitpid(int status);
+extern void mjit_notify_waitpid(int exit_code);
+
+extern void rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
+extern void rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme);
+extern void rb_mjit_before_ractor_spawn(void);
+extern void rb_mjit_constant_state_changed(ID id);
+extern void rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx);
+extern void rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events);
void mjit_child_after_fork(void);
@@ -116,13 +124,18 @@ void mjit_finish(bool close_handle_p);
# else // USE_MJIT
static inline void mjit_cancel_all(const char *reason){}
-static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;}
-static inline void mjit_cont_free(struct mjit_cont *cont){}
static inline void mjit_free_iseq(const rb_iseq_t *iseq){}
static inline void mjit_mark(void){}
static inline VALUE jit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
static inline void mjit_child_after_fork(void){}
+static inline void rb_mjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
+static inline void rb_mjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
+static inline void rb_mjit_before_ractor_spawn(void) {}
+static inline void rb_mjit_constant_state_changed(ID id) {}
+static inline void rb_mjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {}
+static inline void rb_mjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events) {}
+
#define mjit_enabled false
static inline VALUE mjit_pause(bool wait_p){ return Qnil; } // unreachable
static inline VALUE mjit_resume(void){ return Qnil; } // unreachable
diff --git a/mjit.rb b/mjit.rb
index baa107d6dc..717ab832a4 100644
--- a/mjit.rb
+++ b/mjit.rb
@@ -1,13 +1,37 @@
module RubyVM::MJIT
+ # Return true if MJIT is enabled.
def self.enabled?
Primitive.cexpr! 'RBOOL(mjit_enabled)'
end
+ # Stop generating JITed code.
def self.pause(wait: true)
Primitive.cexpr! 'mjit_pause(RTEST(wait))'
end
+ # Start generating JITed code again after pause.
def self.resume
Primitive.cexpr! 'mjit_resume()'
end
end
+
+if RubyVM::MJIT.enabled?
+ begin
+ require 'fiddle'
+ require 'fiddle/import'
+ rescue LoadError
+ return # miniruby doesn't support MJIT
+ end
+
+ # forward declaration for ruby_vm/mjit/compiler
+ RubyVM::MJIT::C = Object.new # :nodoc:
+
+ require 'ruby_vm/mjit/c_type'
+ require 'ruby_vm/mjit/instruction'
+ require 'ruby_vm/mjit/compiler'
+ require 'ruby_vm/mjit/hooks'
+
+ module RubyVM::MJIT
+ private_constant(*constants)
+ end
+end
diff --git a/mjit_c.c b/mjit_c.c
new file mode 100644
index 0000000000..9ba023e84b
--- /dev/null
+++ b/mjit_c.c
@@ -0,0 +1,43 @@
+/**********************************************************************
+
+ mjit_c.c - C helpers for MJIT
+
+ Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
+
+**********************************************************************/
+
+#include "ruby/internal/config.h" // defines USE_MJIT
+
+#if USE_MJIT
+
+#include "mjit.h"
+#include "mjit_c.h"
+#include "internal.h"
+#include "internal/compile.h"
+#include "internal/hash.h"
+#include "yjit.h"
+#include "vm_insnhelper.h"
+
+#include "insns.inc"
+#include "insns_info.inc"
+
+#include "mjit_sp_inc.inc"
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+#define NUM2PTR(x) NUM2ULONG(x)
+#define PTR2NUM(x) ULONG2NUM(x)
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+#define NUM2PTR(x) NUM2ULL(x)
+#define PTR2NUM(x) ULL2NUM(x)
+#endif
+
+// An offsetof implementation that works for unnamed struct and union.
+// Multiplying 8 for compatibility with libclang's offsetof.
+#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
+
+#define SIZEOF(type) RB_SIZE2NUM(sizeof(type))
+#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1))
+
+#include "mjit_c.rbinc"
+
+#endif // USE_MJIT
diff --git a/mjit_c.h b/mjit_c.h
new file mode 100644
index 0000000000..cc4040c9df
--- /dev/null
+++ b/mjit_c.h
@@ -0,0 +1,97 @@
+// This file is parsed by tool/mjit/generate.rb to generate mjit_c.rb
+#ifndef MJIT_C_H
+#define MJIT_C_H
+
+#include "ruby/internal/config.h"
+#include "vm_core.h"
+#include "vm_callinfo.h"
+#include "builtin.h"
+#include "ccan/list/list.h"
+#include "mjit.h"
+#include "shape.h"
+
+// Macros to check if a position is already compiled using compile_status.stack_size_for_pos
+#define NOT_COMPILED_STACK_SIZE -1
+#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
+
+// Linked list of struct rb_mjit_unit.
+struct rb_mjit_unit_list {
+ struct ccan_list_head head;
+ int length; // the list length
+};
+
+enum rb_mjit_unit_type {
+ // Single-ISEQ unit for unit_queue
+ MJIT_UNIT_ISEQ = 0,
+ // Multi-ISEQ unit for mjit_batch
+ MJIT_UNIT_BATCH = 1,
+ // All-ISEQ unit for mjit_compact
+ MJIT_UNIT_COMPACT = 2,
+};
+
+// The unit structure that holds metadata of ISeq for MJIT.
+// TODO: Use different structs for ISEQ and BATCH/COMPACT
+struct rb_mjit_unit {
+ struct ccan_list_node unode;
+ // Unique order number of unit.
+ int id;
+ // Type of this unit
+ enum rb_mjit_unit_type type;
+
+ /* MJIT_UNIT_ISEQ */
+ // ISEQ for a non-batch unit
+ rb_iseq_t *iseq;
+ // Only used by unload_units. Flag to check this unit is currently on stack or not.
+ bool used_code_p;
+ // mjit_compile's optimization switches
+ struct rb_mjit_compile_info compile_info;
+ // captured CC values, they should be marked with iseq.
+ const struct rb_callcache **cc_entries;
+ // ISEQ_BODY(iseq)->ci_size + ones of inlined iseqs
+ unsigned int cc_entries_size;
+
+ /* MJIT_UNIT_BATCH, MJIT_UNIT_COMPACT */
+ // Dlopen handle of the loaded object file.
+ void *handle;
+ // Units compacted by this batch
+ struct rb_mjit_unit_list units; // MJIT_UNIT_BATCH only
+};
+
+// Storage to keep data which is consistent in each conditional branch.
+// This is created and used for one `compile_insns` call and its values
+// should be copied for extra `compile_insns` call.
+struct compile_branch {
+ unsigned int stack_size; // this simulates sp (stack pointer) of YARV
+ bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
+};
+
+// For propagating information needed for lazily pushing a frame.
+struct inlined_call_context {
+ int orig_argc; // ci->orig_argc
+ VALUE me; // vm_cc_cme(cc)
+ int param_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->param.size
+ int local_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->local_table_size
+};
+
+// Storage to keep compiler's status. This should have information
+// which is global during one `mjit_compile` call. Ones conditional
+// in each branch should be stored in `compile_branch`.
+struct compile_status {
+ bool success; // has true if compilation has had no issue
+ int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
+ // If true, JIT-ed code will use local variables to store pushed values instead of
+ // using VM's stack and moving stack pointer.
+ bool local_stack_p;
+ // Index of call cache entries captured to compiled_iseq to be marked on GC
+ int cc_entries_index;
+ // A pointer to root (i.e. not inlined) iseq being compiled.
+ const struct rb_iseq_constant_body *compiled_iseq;
+ int compiled_id; // Just a copy of compiled_iseq->jit_unit->id
+ // Mutated optimization levels
+ struct rb_mjit_compile_info *compile_info;
+ // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there.
+ const struct rb_iseq_constant_body **inlined_iseqs;
+ struct inlined_call_context inline_context;
+};
+
+#endif /* MJIT_C_H */
diff --git a/mjit_c.rb b/mjit_c.rb
new file mode 100644
index 0000000000..966289c5ab
--- /dev/null
+++ b/mjit_c.rb
@@ -0,0 +1,807 @@
+# frozen_string_literal: true
+# Part of this file is generated by tool/mjit/bindgen.rb.
+# Run `make mjit-bindgen` to update code between "MJIT bindgen begin" and "MJIT bindgen end".
+module RubyVM::MJIT # :nodoc: all
+ # This `class << C` section is for calling C functions. For importing variables
+ # or macros as is, please consider using tool/mjit/bindgen.rb instead.
+ class << C
+ def rb_hash_values(cdhash_addr)
+ Primitive.cexpr! 'rb_hash_values((VALUE)NUM2PTR(cdhash_addr))'
+ end
+
+ def builtin_compiler(buf, bf_ptr, index, stack_size, builtin_inline_p)
+ _bf_addr = bf_ptr.to_i
+ # Call "mjit_compile_invokebuiltin_for_#{func}" in mk_builtin_loader.rb
+ Primitive.cstmt! %{
+ RB_BUILTIN bf = (RB_BUILTIN)NUM2PTR(_bf_addr);
+ bf->compiler(buf, NIL_P(index) ? -1 : NUM2LONG(index), NUM2UINT(stack_size), RTEST(builtin_inline_p));
+ return Qnil;
+ }
+ end
+
+ def has_cache_for_send(cc_ptr, insn)
+ _cc_addr = cc_ptr.to_i
+ Primitive.cstmt! %{
+ extern bool rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn);
+ CALL_CACHE cc = (CALL_CACHE)NUM2PTR(_cc_addr);
+ bool has_cache = vm_cc_cme(cc) != NULL &&
+ !(vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_CFUNC && rb_vm_opt_cfunc_p(cc, NUM2INT(insn)));
+ return RBOOL(has_cache);
+ }
+ end
+
+ def rb_shape_get_shape_by_id(shape_id)
+ _shape_id = shape_id.to_i
+ shape_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_shape_get_shape_by_id((shape_id_t)NUM2UINT(_shape_id)))'
+ rb_shape_t.new(shape_addr)
+ end
+
+ def rb_iseq_check(iseq)
+ _iseq_addr = iseq.to_i
+ iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseq_check((rb_iseq_t *)NUM2PTR(_iseq_addr)))'
+ rb_iseq_t.new(iseq_addr)
+ end
+
+ def rb_iseq_path(iseq)
+ _iseq_addr = iseq.to_i
+ Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))'
+ end
+
+ def rb_iseq_first_lineno(iseq)
+ _iseq_addr = iseq.to_i
+ Primitive.cexpr! 'rb_iseq_first_lineno((rb_iseq_t *)NUM2PTR(_iseq_addr))'
+ end
+
+ def vm_ci_argc(ci)
+ _ci_addr = ci.to_i
+ Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))'
+ end
+
+ def vm_ci_flag(ci)
+ _ci_addr = ci.to_i
+ Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))'
+ end
+
+ def rb_splat_or_kwargs_p(ci)
+ _ci_addr = ci.to_i
+ Primitive.cstmt! %{
+ extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci);
+ return RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)));
+ }
+ end
+
+ # Returns true if iseq can use fastpath for setup, otherwise NULL. This becomes true in the same condition
+ # as CC_SET_FASTPATH (in vm_callee_setup_arg) is called from vm_call_iseq_setup.
+ def fastpath_applied_iseq_p(ci_ptr, cc_ptr, iseq_ptr)
+ _ci_addr = ci_ptr.to_i
+ _cc_addr = cc_ptr.to_i
+ _iseq_addr = iseq_ptr.to_i
+ Primitive.cstmt! %q{
+ extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
+ CALL_INFO ci = (CALL_INFO)NUM2PTR(_ci_addr);
+ CALL_CACHE cc = (CALL_CACHE)NUM2PTR(_cc_addr);
+ const rb_iseq_t *iseq = (rb_iseq_t *)NUM2PTR(_iseq_addr);
+
+ bool result = iseq != NULL
+ && !(vm_ci_flag(ci) & VM_CALL_KW_SPLAT) && rb_simple_iseq_p(iseq) // Top of vm_callee_setup_arg. In this case, opt_pc is 0.
+ && vm_ci_argc(ci) == (unsigned int)ISEQ_BODY(iseq)->param.lead_num // exclude argument_arity_error (assumption: `calling->argc == ci->orig_argc` in send insns)
+ && vm_call_iseq_optimizable_p(ci, cc); // CC_SET_FASTPATH condition
+ return RBOOL(result);
+ }
+ end
+
+ def mjit_opts
+ addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)'
+ mjit_options.new(addr)
+ end
+
+ def mjit_call_attribute_sp_inc(insn, operands)
+ _operands_addr = operands.to_i
+ Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))'
+ end
+
+ def mjit_capture_cc_entries(compiled_body, captured_body)
+ _compiled_body_addr = compiled_body.to_i
+ _captured_body_addr = captured_body.to_i
+ Primitive.cstmt! %{
+ extern int mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq);
+ return INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)));
+ }
+ end
+
+ def mjit_cancel_all(reason)
+ Primitive.cstmt! %{
+ mjit_cancel_all(RSTRING_PTR(reason));
+ return Qnil;
+ }
+ end
+
+ # Convert encoded VM pointers to insn BINs.
+ def rb_vm_insn_decode(encoded)
+ Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))'
+ end
+
+ # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging.
+ def rb_vm_insn_encode(bin)
+ Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])'
+ end
+
+ def insn_may_depend_on_sp_or_pc(insn, opes)
+ _opes_addr = opes.to_i
+ Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))'
+ end
+
+ # Convert Integer VALUE to an actual Ruby object
+ def to_ruby(value)
+ Primitive.cexpr! '(VALUE)NUM2PTR(value)'
+ end
+
+ # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging.
+ def rb_iseqw_to_iseq(iseqw)
+ iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))'
+ rb_iseq_t.new(iseq_addr)
+ end
+ end
+
+ ### MJIT bindgen begin ###
+
+ def C.USE_LAZY_LOAD
+ Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) }
+ end
+
+ def C.NOT_COMPILED_STACK_SIZE
+ Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) }
+ end
+
+ def C.VM_CALL_KW_SPLAT
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) }
+ end
+
+ def C.VM_CALL_KW_SPLAT_bit
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT_bit) }
+ end
+
+ def C.VM_CALL_TAILCALL
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) }
+ end
+
+ def C.VM_CALL_TAILCALL_bit
+ Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL_bit) }
+ end
+
+ def C.VM_METHOD_TYPE_CFUNC
+ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_CFUNC) }
+ end
+
+ def C.VM_METHOD_TYPE_ISEQ
+ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) }
+ end
+
+ def C.RUBY_EVENT_CLASS
+ Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
+ end
+
+ def C.SHAPE_CAPACITY_CHANGE
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_CAPACITY_CHANGE) }
+ end
+
+ def C.SHAPE_FLAG_SHIFT
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_FLAG_SHIFT) }
+ end
+
+ def C.SHAPE_FROZEN
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_FROZEN) }
+ end
+
+ def C.SHAPE_ID_NUM_BITS
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_ID_NUM_BITS) }
+ end
+
+ def C.SHAPE_INITIAL_CAPACITY
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_INITIAL_CAPACITY) }
+ end
+
+ def C.SHAPE_IVAR
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_IVAR) }
+ end
+
+ def C.SHAPE_ROOT
+ Primitive.cexpr! %q{ UINT2NUM(SHAPE_ROOT) }
+ end
+
+ def C.INVALID_SHAPE_ID
+ Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
+ end
+
+ def C.SHAPE_MASK
+ Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
+ end
+
+ def C.rb_cFalseClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cFalseClass) }
+ end
+
+ def C.rb_cFloat
+ Primitive.cexpr! %q{ PTR2NUM(rb_cFloat) }
+ end
+
+ def C.rb_cInteger
+ Primitive.cexpr! %q{ PTR2NUM(rb_cInteger) }
+ end
+
+ def C.rb_cNilClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cNilClass) }
+ end
+
+ def C.rb_cSymbol
+ Primitive.cexpr! %q{ PTR2NUM(rb_cSymbol) }
+ end
+
+ def C.rb_cTrueClass
+ Primitive.cexpr! %q{ PTR2NUM(rb_cTrueClass) }
+ end
+
+ def C.CALL_DATA
+ @CALL_DATA ||= self.rb_call_data
+ end
+
+ def C.IC
+ @IC ||= self.iseq_inline_constant_cache
+ end
+
+ def C.IVC
+ @IVC ||= self.iseq_inline_iv_cache_entry
+ end
+
+ def C.RB_BUILTIN
+ @RB_BUILTIN ||= self.rb_builtin_function
+ end
+
+ def C.attr_index_t
+ @attr_index_t ||= CType::Immediate.parse("uint32_t")
+ end
+
+ def C.compile_branch
+ @compile_branch ||= CType::Struct.new(
+ "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"),
+ stack_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)")],
+ finish_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)")],
+ )
+ end
+
+ def C.compile_status
+ @compile_status ||= CType::Struct.new(
+ "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"),
+ success: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)")],
+ stack_size_for_pos: [CType::Pointer.new { CType::Immediate.parse("int") }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)")],
+ local_stack_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)")],
+ cc_entries_index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)")],
+ compiled_iseq: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)")],
+ compiled_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)")],
+ compile_info: [CType::Pointer.new { self.rb_mjit_compile_info }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)")],
+ inlined_iseqs: [CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)")],
+ inline_context: [self.inlined_call_context, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)")],
+ )
+ end
+
+ def C.inlined_call_context
+ @inlined_call_context ||= CType::Struct.new(
+ "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"),
+ orig_argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)")],
+ me: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)")],
+ param_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)")],
+ local_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)")],
+ )
+ end
+
+ def C.iseq_inline_constant_cache
+ @iseq_inline_constant_cache ||= CType::Struct.new(
+ "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"),
+ entry: [CType::Pointer.new { self.iseq_inline_constant_cache_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)")],
+ segments: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)")],
+ )
+ end
+
+ def C.iseq_inline_constant_cache_entry
+ @iseq_inline_constant_cache_entry ||= CType::Struct.new(
+ "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)")],
+ value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)")],
+ _unused1: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)")],
+ _unused2: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)")],
+ ic_cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)")],
+ )
+ end
+
+ def C.iseq_inline_iv_cache_entry
+ @iseq_inline_iv_cache_entry ||= CType::Struct.new(
+ "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"),
+ value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), value)")],
+ iv_set_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), iv_set_name)")],
+ )
+ end
+
+ def C.iseq_inline_storage_entry
+ @iseq_inline_storage_entry ||= CType::Union.new(
+ "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"),
+ once: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"),
+ running_thread: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)")],
+ value: [self.VALUE, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)")],
+ ),
+ ic_cache: self.iseq_inline_constant_cache,
+ iv_cache: self.iseq_inline_iv_cache_entry,
+ )
+ end
+
+ def C.mjit_options
+ @mjit_options ||= CType::Struct.new(
+ "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"),
+ on: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)")],
+ save_temps: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)")],
+ warnings: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)")],
+ debug: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)")],
+ debug_flags: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)")],
+ wait: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)")],
+ call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), call_threshold)")],
+ verbose: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)")],
+ max_cache_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)")],
+ pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)")],
+ custom: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)")],
+ )
+ end
+
+ def C.rb_builtin_function
+ @rb_builtin_function ||= CType::Struct.new(
+ "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"),
+ func_ptr: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)")],
+ argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)")],
+ index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)")],
+ name: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)")],
+ compiler: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)")],
+ )
+ end
+
+ def C.rb_call_data
+ @rb_call_data ||= CType::Struct.new(
+ "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"),
+ ci: [CType::Pointer.new { self.rb_callinfo }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)")],
+ cc: [CType::Pointer.new { self.rb_callcache }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)")],
+ )
+ end
+
+ def C.rb_callable_method_entry_struct
+ @rb_callable_method_entry_struct ||= CType::Struct.new(
+ "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)")],
+ defined_class: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)")],
+ def: [CType::Pointer.new { self.rb_method_definition_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)")],
+ called_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)")],
+ owner: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)")],
+ )
+ end
+
+ def C.rb_callcache
+ @rb_callcache ||= CType::Struct.new(
+ "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)")],
+ klass: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)")],
+ cme_: [CType::Pointer.new { self.rb_callable_method_entry_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)")],
+ call_: [self.vm_call_handler, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)")],
+ aux_: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"),
+ attr: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_.attr)"),
+ value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, value)")],
+ ),
+ method_missing_reason: self.method_missing_reason,
+ v: self.VALUE,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")],
+ )
+ end
+
+ def C.rb_callinfo
+ @rb_callinfo ||= CType::Struct.new(
+ "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)")],
+ kwarg: [CType::Pointer.new { self.rb_callinfo_kwarg }, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)")],
+ mid: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)")],
+ flag: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)")],
+ argc: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)")],
+ )
+ end
+
+ def C.rb_control_frame_t
+ @rb_control_frame_t ||= CType::Struct.new(
+ "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"),
+ pc: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)")],
+ sp: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)")],
+ iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)")],
+ self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)")],
+ ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)")],
+ block_code: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)")],
+ __bp__: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)")],
+ jit_return: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)")],
+ )
+ end
+
+ def C.rb_cref_t
+ @rb_cref_t ||= CType::Struct.new(
+ "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)")],
+ refinements: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)")],
+ klass_or_self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)")],
+ next: [CType::Pointer.new { self.rb_cref_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)")],
+ scope_visi: [self.rb_scope_visibility_t, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)")],
+ )
+ end
+
+ def C.rb_execution_context_struct
+ @rb_execution_context_struct ||= CType::Struct.new(
+ "rb_execution_context_struct", Primitive.cexpr!("SIZEOF(struct rb_execution_context_struct)"),
+ vm_stack: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)")],
+ vm_stack_size: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)")],
+ cfp: [CType::Pointer.new { self.rb_control_frame_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)")],
+ tag: [CType::Pointer.new { self.rb_vm_tag }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)")],
+ interrupt_flag: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)")],
+ interrupt_mask: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)")],
+ fiber_ptr: [CType::Pointer.new { self.rb_fiber_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)")],
+ thread_ptr: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)")],
+ local_storage: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)")],
+ local_storage_recursive_hash: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)")],
+ local_storage_recursive_hash_for_trace: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)")],
+ storage: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), storage)")],
+ root_lep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)")],
+ root_svar: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)")],
+ ensure_list: [CType::Pointer.new { self.rb_ensure_list_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)")],
+ trace_arg: [CType::Pointer.new { self.rb_trace_arg_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)")],
+ errinfo: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)")],
+ passed_block_handler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)")],
+ raised_flag: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)")],
+ private_const_reference: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)")],
+ machine: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"),
+ stack_start: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)")],
+ stack_end: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)")],
+ stack_maxsize: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)")],
+ )
+ end
+
+ def C.rb_execution_context_t
+ @rb_execution_context_t ||= self.rb_execution_context_struct
+ end
+
+ def C.rb_iseq_constant_body
+ @rb_iseq_constant_body ||= CType::Struct.new(
+ "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"),
+ type: [self.rb_iseq_type, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)")],
+ iseq_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)")],
+ iseq_encoded: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)")],
+ param: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"),
+ flags: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"),
+ has_lead: [CType::BitField.new(1, 0), 0],
+ has_opt: [CType::BitField.new(1, 1), 1],
+ has_rest: [CType::BitField.new(1, 2), 2],
+ has_post: [CType::BitField.new(1, 3), 3],
+ has_kw: [CType::BitField.new(1, 4), 4],
+ has_kwrest: [CType::BitField.new(1, 5), 5],
+ has_block: [CType::BitField.new(1, 6), 6],
+ ambiguous_param0: [CType::BitField.new(1, 7), 7],
+ accepts_no_kwarg: [CType::BitField.new(1, 0), 8],
+ ruby2_keywords: [CType::BitField.new(1, 1), 9],
+ ), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")],
+ size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")],
+ lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")],
+ opt_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)")],
+ rest_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)")],
+ post_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)")],
+ post_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)")],
+ block_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)")],
+ opt_table: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)")],
+ keyword: [CType::Pointer.new { self.rb_iseq_param_keyword }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)")],
+ location: [self.rb_iseq_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)")],
+ insns_info: [self.iseq_insn_info, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)")],
+ local_table: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)")],
+ catch_table: [CType::Pointer.new { self.iseq_catch_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)")],
+ parent_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)")],
+ local_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)")],
+ is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)")],
+ call_data: [CType::Pointer.new { self.rb_call_data }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)")],
+ variable: [CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"),
+ flip_count: [self.rb_snum_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)")],
+ script_lines: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)")],
+ coverage: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)")],
+ pc2branchindex: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)")],
+ original_iseq: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)")],
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)")],
+ local_table_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)")],
+ ic_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)")],
+ ise_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)")],
+ ivc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)")],
+ icvarc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)")],
+ ci_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)")],
+ stack_max: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)")],
+ catch_except_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)")],
+ builtin_inline_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)")],
+ mark_bits: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"),
+ list: CType::Pointer.new { self.iseq_bits_t },
+ single: self.iseq_bits_t,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")],
+ outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")],
+ mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")],
+ jit_func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")],
+ total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")],
+ mjit_unit: [CType::Pointer.new { self.rb_mjit_unit }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mjit_unit)")],
+ )
+ end
+
+ def C.rb_iseq_location_t
+ @rb_iseq_location_t ||= CType::Struct.new(
+ "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"),
+ pathobj: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), true],
+ base_label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), true],
+ label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), true],
+ first_lineno: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true],
+ node_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)")],
+ code_location: [self.rb_code_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)")],
+ )
+ end
+
+ def C.rb_iseq_struct
+ @rb_iseq_struct ||= CType::Struct.new(
+ "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"),
+ flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)")],
+ wrapper: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)")],
+ body: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)")],
+ aux: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"),
+ compile_data: CType::Pointer.new { self.iseq_compile_data },
+ loader: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"),
+ obj: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)")],
+ index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)")],
+ ),
+ exec: CType::Struct.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"),
+ local_hooks: [CType::Pointer.new { self.rb_hook_list_struct }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)")],
+ global_trace_events: [self.rb_event_flag_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)")],
+ ),
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)")],
+ )
+ end
+
+ def C.rb_iseq_t
+ @rb_iseq_t ||= self.rb_iseq_struct
+ end
+
+ def C.rb_method_definition_struct
+ @rb_method_definition_struct ||= CType::Struct.new(
+ "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"),
+ type: [CType::BitField.new(4, 0), 0],
+ iseq_overload: [CType::BitField.new(1, 4), 4],
+ no_redef_warning: [CType::BitField.new(1, 5), 5],
+ aliased: [CType::BitField.new(1, 6), 6],
+ reference_count: [CType::BitField.new(28, 0), 32],
+ body: [CType::Union.new(
+ "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"),
+ iseq: self.rb_method_iseq_t,
+ cfunc: self.rb_method_cfunc_t,
+ attr: self.rb_method_attr_t,
+ alias: self.rb_method_alias_t,
+ refined: self.rb_method_refined_t,
+ bmethod: self.rb_method_bmethod_t,
+ optimized: self.rb_method_optimized_t,
+ ), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)")],
+ original_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)")],
+ method_serial: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)")],
+ )
+ end
+
+ def C.rb_method_iseq_t
+ @rb_method_iseq_t ||= CType::Struct.new(
+ "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"),
+ iseqptr: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)")],
+ cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)")],
+ )
+ end
+
+ def C.rb_method_type_t
+ @rb_method_type_t ||= CType::Immediate.parse("int")
+ end
+
+ def C.rb_mjit_compile_info
+ @rb_mjit_compile_info ||= CType::Struct.new(
+ "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"),
+ disable_ivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)")],
+ disable_exivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)")],
+ disable_send_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)")],
+ disable_inlining: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)")],
+ disable_const_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)")],
+ )
+ end
+
+ def C.rb_mjit_unit
+ @rb_mjit_unit ||= CType::Struct.new(
+ "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"),
+ unode: [self.ccan_list_node, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)")],
+ id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)")],
+ type: [self.rb_mjit_unit_type, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), type)")],
+ iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)")],
+ used_code_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)")],
+ compile_info: [self.rb_mjit_compile_info, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)")],
+ cc_entries: [CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)")],
+ cc_entries_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)")],
+ handle: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)")],
+ units: [self.rb_mjit_unit_list, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), units)")],
+ )
+ end
+
+ def C.rb_serial_t
+ @rb_serial_t ||= CType::Immediate.parse("unsigned long long")
+ end
+
+ def C.rb_shape
+ @rb_shape ||= CType::Struct.new(
+ "rb_shape", Primitive.cexpr!("SIZEOF(struct rb_shape)"),
+ edges: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edges)")],
+ edge_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edge_name)")],
+ next_iv_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), next_iv_index)")],
+ capacity: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), capacity)")],
+ type: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), type)")],
+ size_pool_index: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), size_pool_index)")],
+ parent_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), parent_id)")],
+ )
+ end
+
+ def C.rb_shape_t
+ @rb_shape_t ||= self.rb_shape
+ end
+
+ def C.VALUE
+ @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)"))
+ end
+
+ def C.shape_id_t
+ @shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)"))
+ end
+
+ def C._Bool
+ CType::Bool.new
+ end
+
+ def C.ID
+ CType::Stub.new(:ID)
+ end
+
+ def C.rb_thread_struct
+ CType::Stub.new(:rb_thread_struct)
+ end
+
+ def C.vm_call_handler
+ CType::Stub.new(:vm_call_handler)
+ end
+
+ def C.method_missing_reason
+ CType::Stub.new(:method_missing_reason)
+ end
+
+ def C.rb_callinfo_kwarg
+ CType::Stub.new(:rb_callinfo_kwarg)
+ end
+
+ def C.rb_cref_struct
+ CType::Stub.new(:rb_cref_struct)
+ end
+
+ def C.rb_scope_visibility_t
+ CType::Stub.new(:rb_scope_visibility_t)
+ end
+
+ def C.rb_vm_tag
+ CType::Stub.new(:rb_vm_tag)
+ end
+
+ def C.rb_atomic_t
+ CType::Stub.new(:rb_atomic_t)
+ end
+
+ def C.rb_fiber_t
+ CType::Stub.new(:rb_fiber_t)
+ end
+
+ def C.rb_id_table
+ CType::Stub.new(:rb_id_table)
+ end
+
+ def C.rb_ensure_list_t
+ CType::Stub.new(:rb_ensure_list_t)
+ end
+
+ def C.rb_trace_arg_struct
+ CType::Stub.new(:rb_trace_arg_struct)
+ end
+
+ def C.rb_iseq_type
+ CType::Stub.new(:rb_iseq_type)
+ end
+
+ def C.rb_iseq_param_keyword
+ CType::Stub.new(:rb_iseq_param_keyword)
+ end
+
+ def C.iseq_insn_info
+ CType::Stub.new(:iseq_insn_info)
+ end
+
+ def C.iseq_catch_table
+ CType::Stub.new(:iseq_catch_table)
+ end
+
+ def C.rb_snum_t
+ CType::Stub.new(:rb_snum_t)
+ end
+
+ def C.iseq_bits_t
+ CType::Stub.new(:iseq_bits_t)
+ end
+
+ def C.rb_code_location_t
+ CType::Stub.new(:rb_code_location_t)
+ end
+
+ def C.iseq_compile_data
+ CType::Stub.new(:iseq_compile_data)
+ end
+
+ def C.rb_hook_list_struct
+ CType::Stub.new(:rb_hook_list_struct)
+ end
+
+ def C.rb_event_flag_t
+ CType::Stub.new(:rb_event_flag_t)
+ end
+
+ def C.rb_method_cfunc_t
+ CType::Stub.new(:rb_method_cfunc_t)
+ end
+
+ def C.rb_method_attr_t
+ CType::Stub.new(:rb_method_attr_t)
+ end
+
+ def C.rb_method_alias_t
+ CType::Stub.new(:rb_method_alias_t)
+ end
+
+ def C.rb_method_refined_t
+ CType::Stub.new(:rb_method_refined_t)
+ end
+
+ def C.rb_method_bmethod_t
+ CType::Stub.new(:rb_method_bmethod_t)
+ end
+
+ def C.rb_method_optimized_t
+ CType::Stub.new(:rb_method_optimized_t)
+ end
+
+ def C.ccan_list_node
+ CType::Stub.new(:ccan_list_node)
+ end
+
+ def C.rb_mjit_unit_type
+ CType::Stub.new(:rb_mjit_unit_type)
+ end
+
+ def C.rb_mjit_unit_list
+ CType::Stub.new(:rb_mjit_unit_list)
+ end
+
+ ### MJIT bindgen end ###
+end if RubyVM::MJIT.enabled? && RubyVM::MJIT.const_defined?(:C) # not defined for miniruby
diff --git a/mjit_compiler.c b/mjit_compiler.c
deleted file mode 100644
index c02397e53c..0000000000
--- a/mjit_compiler.c
+++ /dev/null
@@ -1,596 +0,0 @@
-/**********************************************************************
-
- mjit_compiler.c - MRI method JIT compiler
-
- Copyright (C) 2017 Takashi Kokubun <takashikkbn@gmail.com>.
-
-**********************************************************************/
-
-// NOTE: All functions in this file are executed on MJIT worker. So don't
-// call Ruby methods (C functions that may call rb_funcall) or trigger
-// GC (using ZALLOC, xmalloc, xfree, etc.) in this file.
-
-#include "ruby/internal/config.h" // defines USE_MJIT
-
-#if USE_MJIT
-
-#include "internal.h"
-#include "internal/compile.h"
-#include "internal/hash.h"
-#include "internal/object.h"
-#include "internal/variable.h"
-#include "mjit.h"
-#include "mjit_unit.h"
-#include "yjit.h"
-#include "vm_core.h"
-#include "vm_callinfo.h"
-#include "vm_exec.h"
-#include "vm_insnhelper.h"
-
-#include "builtin.h"
-#include "insns.inc"
-#include "insns_info.inc"
-
-// Macros to check if a position is already compiled using compile_status.stack_size_for_pos
-#define NOT_COMPILED_STACK_SIZE -1
-#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
-
-// For propagating information needed for lazily pushing a frame.
-struct inlined_call_context {
- int orig_argc; // ci->orig_argc
- VALUE me; // vm_cc_cme(cc)
- int param_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->param.size
- int local_size; // def_iseq_ptr(vm_cc_cme(cc)->def)->body->local_table_size
-};
-
-// Storage to keep compiler's status. This should have information
-// which is global during one `mjit_compile` call. Ones conditional
-// in each branch should be stored in `compile_branch`.
-struct compile_status {
- bool success; // has true if compilation has had no issue
- int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
- // If true, JIT-ed code will use local variables to store pushed values instead of
- // using VM's stack and moving stack pointer.
- bool local_stack_p;
- // Safely-accessible ivar cache entries copied from main thread.
- union iseq_inline_storage_entry *is_entries;
- // Index of call cache entries captured to compiled_iseq to be marked on GC
- int cc_entries_index;
- // A pointer to root (i.e. not inlined) iseq being compiled.
- const struct rb_iseq_constant_body *compiled_iseq;
- int compiled_id; // Just a copy of compiled_iseq->jit_unit->id
- // Mutated optimization levels
- struct rb_mjit_compile_info *compile_info;
- bool merge_ivar_guards_p; // If true, merge guards of ivar accesses
- rb_serial_t ivar_serial; // ic_serial of IVC in is_entries (used only when merge_ivar_guards_p)
- size_t max_ivar_index; // Max IVC index in is_entries (used only when merge_ivar_guards_p)
- // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there.
- const struct rb_iseq_constant_body **inlined_iseqs;
- struct inlined_call_context inline_context;
-};
-
-// Storage to keep data which is consistent in each conditional branch.
-// This is created and used for one `compile_insns` call and its values
-// should be copied for extra `compile_insns` call.
-struct compile_branch {
- unsigned int stack_size; // this simulates sp (stack pointer) of YARV
- bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
-};
-
-struct case_dispatch_var {
- FILE *f;
- unsigned int base_pos;
- VALUE last_value;
-};
-
-static size_t
-call_data_index(CALL_DATA cd, const struct rb_iseq_constant_body *body)
-{
- return cd - body->call_data;
-}
-
-// Using this function to refer to cc_entries allocated by `mjit_capture_cc_entries`
-// instead of storing cc_entries in status directly so that we always refer to a new address
-// returned by `realloc` inside it.
-static const struct rb_callcache **
-captured_cc_entries(const struct compile_status *status)
-{
- VM_ASSERT(status->cc_entries_index != -1);
- return status->compiled_iseq->jit_unit->cc_entries + status->cc_entries_index;
-}
-
-// Returns true if call cache is still not obsoleted and vm_cc_cme(cc)->def->type is available.
-static bool
-has_valid_method_type(CALL_CACHE cc)
-{
- return vm_cc_cme(cc) != NULL;
-}
-
-// Returns true if MJIT thinks this cc's opt_* insn may fallback to opt_send_without_block.
-static bool
-has_cache_for_send(CALL_CACHE cc, int insn)
-{
- extern bool rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn);
- return has_valid_method_type(cc) &&
- !(vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_CFUNC && rb_vm_opt_cfunc_p(cc, insn));
-}
-
-// Returns true if iseq can use fastpath for setup, otherwise NULL. This becomes true in the same condition
-// as CC_SET_FASTPATH (in vm_callee_setup_arg) is called from vm_call_iseq_setup.
-static bool
-fastpath_applied_iseq_p(const CALL_INFO ci, const CALL_CACHE cc, const rb_iseq_t *iseq)
-{
- extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
- return iseq != NULL
- && !(vm_ci_flag(ci) & VM_CALL_KW_SPLAT) && rb_simple_iseq_p(iseq) // Top of vm_callee_setup_arg. In this case, opt_pc is 0.
- && vm_ci_argc(ci) == (unsigned int)ISEQ_BODY(iseq)->param.lead_num // exclude argument_arity_error (assumption: `calling->argc == ci->orig_argc` in send insns)
- && vm_call_iseq_optimizable_p(ci, cc); // CC_SET_FASTPATH condition
-}
-
-// Return true if an object of the klass may be a special const. See: rb_class_of
-static bool
-maybe_special_const_class_p(const VALUE klass)
-{
- return klass == rb_cFalseClass
- || klass == rb_cNilClass
- || klass == rb_cTrueClass
- || klass == rb_cInteger
- || klass == rb_cSymbol
- || klass == rb_cFloat;
-}
-
-static int
-compile_case_dispatch_each(VALUE key, VALUE value, VALUE arg)
-{
- struct case_dispatch_var *var = (struct case_dispatch_var *)arg;
- unsigned int offset;
-
- if (var->last_value != value) {
- offset = FIX2INT(value);
- var->last_value = value;
- fprintf(var->f, " case %d:\n", offset);
- fprintf(var->f, " goto label_%d;\n", var->base_pos + offset);
- fprintf(var->f, " break;\n");
- }
- return ST_CONTINUE;
-}
-
-// Calling rb_id2str in MJIT worker causes random SEGV. So this is disabled by default.
-static void
-comment_id(FILE *f, ID id)
-{
-#ifdef MJIT_COMMENT_ID
- VALUE name = rb_id2str(id);
- const char *p, *e;
- char c, prev = '\0';
-
- if (!name) return;
- p = RSTRING_PTR(name);
- e = RSTRING_END(name);
- fputs("/* :\"", f);
- for (; p < e; ++p) {
- switch (c = *p) {
- case '*': case '/': if (prev != (c ^ ('/' ^ '*'))) break;
- case '\\': case '"': fputc('\\', f);
- }
- fputc(c, f);
- prev = c;
- }
- fputs("\" */", f);
-#endif
-}
-
-static void compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
- unsigned int pos, struct compile_status *status);
-
-// Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
-// b->stack_size and return next position.
-//
-// When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
-// it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
-// does not have it can be compiled as usual.
-static unsigned int
-compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn, const VALUE *operands,
- const unsigned int pos, struct compile_status *status, struct compile_branch *b)
-{
- unsigned int next_pos = pos + insn_len(insn);
-
-/*****************/
- #include "mjit_compile.inc"
-/*****************/
-
- // If next_pos is already compiled and this branch is not finished yet,
- // next instruction won't be compiled in C code next and will need `goto`.
- if (!b->finish_p && next_pos < body->iseq_size && ALREADY_COMPILED_P(status, next_pos)) {
- fprintf(f, "goto label_%d;\n", next_pos);
-
- // Verify stack size assumption is the same among multiple branches
- if ((unsigned int)status->stack_size_for_pos[next_pos] != b->stack_size) {
- if (mjit_opts.warnings || mjit_opts.verbose)
- fprintf(stderr, "MJIT warning: JIT stack assumption is not the same between branches (%d != %u)\n",
- status->stack_size_for_pos[next_pos], b->stack_size);
- status->success = false;
- }
- }
-
- return next_pos;
-}
-
-// Compile one conditional branch. If it has branchXXX insn, this should be
-// called multiple times for each branch.
-static void
-compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
- unsigned int pos, struct compile_status *status)
-{
- struct compile_branch branch;
-
- branch.stack_size = stack_size;
- branch.finish_p = false;
-
- while (pos < body->iseq_size && !ALREADY_COMPILED_P(status, pos) && !branch.finish_p) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- status->stack_size_for_pos[pos] = (int)branch.stack_size;
-
- fprintf(f, "\nlabel_%d: /* %s */\n", pos, insn_name(insn));
- pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch);
- if (status->success && branch.stack_size > body->stack_max) {
- if (mjit_opts.warnings || mjit_opts.verbose)
- fprintf(stderr, "MJIT warning: JIT stack size (%d) exceeded its max size (%d)\n", branch.stack_size, body->stack_max);
- status->success = false;
- }
- if (!status->success)
- break;
- }
-}
-
-// Print the block to cancel inlined method call. It's supporting only `opt_send_without_block` for now.
-static void
-compile_inlined_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct inlined_call_context *inline_context)
-{
- fprintf(f, "\ncancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel);\n");
- fprintf(f, " rb_mjit_recompile_inlining(original_iseq);\n");
-
- // Swap pc/sp set on cancel with original pc/sp.
- fprintf(f, " const VALUE *current_pc = reg_cfp->pc;\n");
- fprintf(f, " VALUE *current_sp = reg_cfp->sp;\n");
- fprintf(f, " reg_cfp->pc = orig_pc;\n");
- fprintf(f, " reg_cfp->sp = orig_sp;\n\n");
-
- // Lazily push the current call frame.
- fprintf(f, " struct rb_calling_info calling;\n");
- fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"); // assumes `opt_send_without_block`
- fprintf(f, " calling.argc = %d;\n", inline_context->orig_argc);
- fprintf(f, " calling.recv = reg_cfp->self;\n");
- fprintf(f, " reg_cfp->self = orig_self;\n");
- fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, (const rb_callable_method_entry_t *)0x%"PRIxVALUE", 0, %d, %d);\n\n",
- inline_context->me, inline_context->param_size, inline_context->local_size); // fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
-
- // Start usual cancel from here.
- fprintf(f, " reg_cfp = ec->cfp;\n"); // work on the new frame
- fprintf(f, " reg_cfp->pc = current_pc;\n");
- fprintf(f, " reg_cfp->sp = current_sp;\n");
- for (unsigned int i = 0; i < body->stack_max; i++) { // should be always `status->local_stack_p`
- fprintf(f, " *(vm_base_ptr(reg_cfp) + %d) = stack[%d];\n", i, i);
- }
- // We're not just returning Qundef here so that caller's normal cancel handler can
- // push back `stack` to `cfp->sp`.
- fprintf(f, " return vm_exec(ec, false);\n");
-}
-
-// Print the block to cancel JIT execution.
-static void
-compile_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct compile_status *status)
-{
- if (status->inlined_iseqs == NULL) { // the current ISeq is being inlined
- compile_inlined_cancel_handler(f, body, &status->inline_context);
- return;
- }
-
- fprintf(f, "\nsend_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_send_inline);\n");
- fprintf(f, " rb_mjit_recompile_send(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nivar_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_ivar_inline);\n");
- fprintf(f, " rb_mjit_recompile_ivar(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nexivar_cancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_exivar_inline);\n");
- fprintf(f, " rb_mjit_recompile_exivar(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\nconst_cancel:\n");
- fprintf(f, " rb_mjit_recompile_const(original_iseq);\n");
- fprintf(f, " goto cancel;\n");
-
- fprintf(f, "\ncancel:\n");
- fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel);\n");
- if (status->local_stack_p) {
- for (unsigned int i = 0; i < body->stack_max; i++) {
- fprintf(f, " *(vm_base_ptr(reg_cfp) + %d) = stack[%d];\n", i, i);
- }
- }
- fprintf(f, " return Qundef;\n");
-}
-
-extern int
-mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const struct rb_iseq_constant_body *captured_iseq);
-
-// Copy current is_entries and use it throughout the current compilation consistently.
-// While ic->entry has been immutable since https://github.com/ruby/ruby/pull/3662,
-// we still need this to avoid a race condition between entries and ivar_serial/max_ivar_index.
-static void
-mjit_capture_is_entries(const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries)
-{
- if (is_entries == NULL)
- return;
- memcpy(is_entries, body->is_entries, sizeof(union iseq_inline_storage_entry) * ISEQ_IS_SIZE(body));
-}
-
-static bool
-mjit_compile_body(FILE *f, const rb_iseq_t *iseq, struct compile_status *status)
-{
- const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- status->success = true;
- status->local_stack_p = !body->catch_except_p;
-
- if (status->local_stack_p) {
- fprintf(f, " VALUE stack[%d];\n", body->stack_max);
- }
- else {
- fprintf(f, " VALUE *stack = reg_cfp->sp;\n");
- }
- if (status->inlined_iseqs != NULL) // i.e. compile root
- fprintf(f, " static const rb_iseq_t *original_iseq = (const rb_iseq_t *)0x%"PRIxVALUE";\n", (VALUE)iseq);
- fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
- (VALUE)body->iseq_encoded);
- fprintf(f, " VALUE cfp_self = reg_cfp->self;\n"); // cache self across the method
- fprintf(f, "#undef GET_SELF\n");
- fprintf(f, "#define GET_SELF() cfp_self\n");
-
- // Generate merged ivar guards first if needed
- if (!status->compile_info->disable_ivar_cache && status->merge_ivar_guards_p) {
- fprintf(f, " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&", status->ivar_serial);
-#if USE_RVARGC
- fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj)
-#else
- if (status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX) {
- fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
- }
- else {
- fprintf(f, "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())"); // index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
- }
-#endif
- fprintf(f, "))) {\n");
- fprintf(f, " goto ivar_cancel;\n");
- fprintf(f, " }\n");
- }
-
- // Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
- // are not considered since vm_exec doesn't call jit_exec for catch tables.
- if (body->param.flags.has_opt) {
- int i;
- fprintf(f, "\n");
- fprintf(f, " switch (reg_cfp->pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded) {\n");
- for (i = 0; i <= body->param.opt_num; i++) {
- VALUE pc_offset = body->param.opt_table[i];
- fprintf(f, " case %"PRIdVALUE":\n", pc_offset);
- fprintf(f, " goto label_%"PRIdVALUE";\n", pc_offset);
- }
- fprintf(f, " }\n");
- }
-
- compile_insns(f, body, 0, 0, status);
- compile_cancel_handler(f, body, status);
- fprintf(f, "#undef GET_SELF");
- return status->success;
-}
-
-// Return true if the ISeq can be inlined without pushing a new control frame.
-static bool
-inlinable_iseq_p(const struct rb_iseq_constant_body *body)
-{
- // 1) If catch_except_p, caller frame should be preserved when callee catches an exception.
- // Then we need to wrap `vm_exec()` but then we can't inline the call inside it.
- //
- // 2) If `body->catch_except_p` is false and `handles_sp?` of an insn is false,
- // sp is not moved as we assume `status->local_stack_p = !body->catch_except_p`.
- //
- // 3) If `body->catch_except_p` is false and `always_leaf?` of an insn is true,
- // pc is not moved.
- if (body->catch_except_p)
- return false;
-
- unsigned int pos = 0;
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- // All insns in the ISeq except `leave` (to be overridden in the inlined code)
- // should meet following strong assumptions:
- // * Do not require `cfp->sp` motion
- // * Do not move `cfp->pc`
- // * Do not read any `cfp->pc`
- if (insn == BIN(invokebuiltin) || insn == BIN(opt_invokebuiltin_delegate) || insn == BIN(opt_invokebuiltin_delegate_leave)) {
- // builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq
- if (!body->builtin_inline_p)
- return false;
- }
- else if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1)))
- return false;
- // At this moment, `cfp->ep` in an inlined method is not working.
- switch (insn) {
- case BIN(getlocal):
- case BIN(getlocal_WC_0):
- case BIN(getlocal_WC_1):
- case BIN(setlocal):
- case BIN(setlocal_WC_0):
- case BIN(setlocal_WC_1):
- case BIN(getblockparam):
- case BIN(getblockparamproxy):
- case BIN(setblockparam):
- return false;
- }
- pos += insn_len(insn);
- }
- return true;
-}
-
-// Return an iseq pointer if cc has inlinable iseq.
-const rb_iseq_t *
-rb_mjit_inlinable_iseq(const struct rb_callinfo *ci, const struct rb_callcache *cc)
-{
- const rb_iseq_t *iseq;
- if (has_valid_method_type(cc) &&
- !(vm_ci_flag(ci) & VM_CALL_TAILCALL) && // inlining only non-tailcall path
- vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_ISEQ &&
- fastpath_applied_iseq_p(ci, cc, iseq = def_iseq_ptr(vm_cc_cme(cc)->def)) &&
- // CC_SET_FASTPATH in vm_callee_setup_arg
- inlinable_iseq_p(ISEQ_BODY(iseq))) {
- return iseq;
- }
- return NULL;
-}
-
-static void
-init_ivar_compile_status(const struct rb_iseq_constant_body *body, struct compile_status *status)
-{
- mjit_capture_is_entries(body, status->is_entries);
-
- int num_ivars = 0;
- unsigned int pos = 0;
- status->max_ivar_index = 0;
- status->ivar_serial = 0;
-
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- if (insn == BIN(getinstancevariable) || insn == BIN(setinstancevariable)) {
- IVC ic = (IVC)body->iseq_encoded[pos+2];
- IVC ic_copy = &(status->is_entries + ((union iseq_inline_storage_entry *)ic - body->is_entries))->iv_cache;
- if (ic_copy->entry) { // Only initialized (ic_serial > 0) IVCs are optimized
- num_ivars++;
-
- if (status->max_ivar_index < ic_copy->entry->index) {
- status->max_ivar_index = ic_copy->entry->index;
- }
-
- if (status->ivar_serial == 0) {
- status->ivar_serial = ic_copy->entry->class_serial;
- }
- else if (status->ivar_serial != ic_copy->entry->class_serial) {
- // Multiple classes have used this ISeq. Give up assuming one serial.
- status->merge_ivar_guards_p = false;
- return;
- }
- }
- }
- pos += insn_len(insn);
- }
- status->merge_ivar_guards_p = status->ivar_serial > 0 && num_ivars >= 2;
-}
-
-// This needs to be macro instead of a function because it's using `alloca`.
-#define INIT_COMPILE_STATUS(status, body, compile_root_p) do { \
- status = (struct compile_status){ \
- .stack_size_for_pos = (int *)alloca(sizeof(int) * body->iseq_size), \
- .inlined_iseqs = compile_root_p ? \
- alloca(sizeof(const struct rb_iseq_constant_body *) * body->iseq_size) : NULL, \
- .is_entries = (ISEQ_IS_SIZE(body) > 0) ? \
- alloca(sizeof(union iseq_inline_storage_entry) * ISEQ_IS_SIZE(body)) : NULL, \
- .cc_entries_index = (body->ci_size > 0) ? \
- mjit_capture_cc_entries(status.compiled_iseq, body) : -1, \
- .compiled_id = status.compiled_id, \
- .compiled_iseq = status.compiled_iseq, \
- .compile_info = compile_root_p ? \
- rb_mjit_iseq_compile_info(body) : alloca(sizeof(struct rb_mjit_compile_info)) \
- }; \
- memset(status.stack_size_for_pos, NOT_COMPILED_STACK_SIZE, sizeof(int) * body->iseq_size); \
- if (compile_root_p) \
- memset((void *)status.inlined_iseqs, 0, sizeof(const struct rb_iseq_constant_body *) * body->iseq_size); \
- else \
- memset(status.compile_info, 0, sizeof(struct rb_mjit_compile_info)); \
-} while (0)
-
-static bool
-precompile_inlinable_child_iseq(FILE *f, const rb_iseq_t *child_iseq, struct compile_status *status,
- const struct rb_callinfo *ci, const struct rb_callcache *cc, unsigned int pos)
-{
- struct compile_status child_status = { .compiled_iseq = status->compiled_iseq, .compiled_id = status->compiled_id };
- INIT_COMPILE_STATUS(child_status, ISEQ_BODY(child_iseq), false);
- child_status.inline_context = (struct inlined_call_context){
- .orig_argc = vm_ci_argc(ci),
- .me = (VALUE)vm_cc_cme(cc),
- .param_size = ISEQ_BODY(child_iseq)->param.size,
- .local_size = ISEQ_BODY(child_iseq)->local_table_size
- };
- if (ISEQ_BODY(child_iseq)->ci_size > 0 && child_status.cc_entries_index == -1) {
- return false;
- }
- init_ivar_compile_status(ISEQ_BODY(child_iseq), &child_status);
-
- fprintf(f, "ALWAYS_INLINE(static VALUE _mjit%d_inlined_%d(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n", status->compiled_id, pos);
- fprintf(f, "static inline VALUE\n_mjit%d_inlined_%d(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq)\n{\n", status->compiled_id, pos);
- fprintf(f, " const VALUE *orig_pc = reg_cfp->pc;\n");
- fprintf(f, " VALUE *orig_sp = reg_cfp->sp;\n");
- bool success = mjit_compile_body(f, child_iseq, &child_status);
- fprintf(f, "\n} /* end of _mjit%d_inlined_%d */\n\n", status->compiled_id, pos);
-
- return success;
-}
-
-// Compile inlinable ISeqs to C code in `f`. It returns true if it succeeds to compile them.
-static bool
-precompile_inlinable_iseqs(FILE *f, const rb_iseq_t *iseq, struct compile_status *status)
-{
- const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- unsigned int pos = 0;
- while (pos < body->iseq_size) {
- int insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
- if (insn == BIN(opt_send_without_block) || insn == BIN(opt_size)) { // `compile_inlined_cancel_handler` supports only `opt_send_without_block`
- CALL_DATA cd = (CALL_DATA)body->iseq_encoded[pos + 1];
- const struct rb_callinfo *ci = cd->ci;
- const struct rb_callcache *cc = captured_cc_entries(status)[call_data_index(cd, body)]; // use copy to avoid race condition
-
- const rb_iseq_t *child_iseq;
- if ((child_iseq = rb_mjit_inlinable_iseq(ci, cc)) != NULL) {
- status->inlined_iseqs[pos] = ISEQ_BODY(child_iseq);
-
- if (mjit_opts.verbose >= 1) // print beforehand because ISeq may be GCed during copy job.
- fprintf(stderr, "JIT inline: %s@%s:%d => %s@%s:%d\n",
- RSTRING_PTR(ISEQ_BODY(child_iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(child_iseq)), FIX2INT(ISEQ_BODY(child_iseq)->location.first_lineno),
- RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
- RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
- if (!precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos))
- return false;
- }
- }
- pos += insn_len(insn);
- }
- return true;
-}
-
-// Compile ISeq to C code in `f`. It returns true if it succeeds to compile.
-bool
-mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
-{
- struct compile_status status = { .compiled_iseq = ISEQ_BODY(iseq), .compiled_id = id };
- INIT_COMPILE_STATUS(status, ISEQ_BODY(iseq), true);
- if (ISEQ_BODY(iseq)->ci_size > 0 && status.cc_entries_index == -1) {
- return false;
- }
- init_ivar_compile_status(ISEQ_BODY(iseq), &status);
-
- if (!status.compile_info->disable_send_cache && !status.compile_info->disable_inlining) {
- if (!precompile_inlinable_iseqs(f, iseq, &status))
- return false;
- }
-
- fprintf(f, "VALUE\n%s(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n", funcname);
- bool success = mjit_compile_body(f, iseq, &status);
- fprintf(f, "\n} // end of %s\n", funcname);
- return success;
-}
-
-#endif // USE_MJIT
diff --git a/mjit_unit.h b/mjit_unit.h
deleted file mode 100644
index 2e23a8d5fc..0000000000
--- a/mjit_unit.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef INTERNAL_MJIT_H
-#define INTERNAL_MJIT_H
-
-#include "ccan/list/list.h"
-
-// The unit structure that holds metadata of ISeq for MJIT.
-struct rb_mjit_unit {
- struct ccan_list_node unode;
- // Unique order number of unit.
- int id;
- // Dlopen handle of the loaded object file.
- void *handle;
- rb_iseq_t *iseq;
-#if defined(_WIN32)
- // DLL cannot be removed while loaded on Windows. If this is set, it'll be lazily deleted.
- char *so_file;
-#endif
- // Only used by unload_units. Flag to check this unit is currently on stack or not.
- bool used_code_p;
- // True if it's a unit for JIT compaction
- bool compact_p;
- // mjit_compile's optimization switches
- struct rb_mjit_compile_info compile_info;
- // captured CC values, they should be marked with iseq.
- const struct rb_callcache **cc_entries;
- unsigned int cc_entries_size; // ISEQ_BODY(iseq)->ci_size + ones of inlined iseqs
-};
-
-#endif /* INTERNAL_MJIT_H */
diff --git a/node.c b/node.c
index 483e7fa8fb..a6cb498778 100644
--- a/node.c
+++ b/node.c
@@ -1098,6 +1098,9 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
F_NODE(nd_pkwrestarg, "keyword rest argument");
}
return;
+ case NODE_ERROR:
+ ANN("Broken input recovered by Error Tolerant mode");
+ return;
case NODE_ARGS_AUX:
case NODE_LAST:
@@ -1158,6 +1161,12 @@ struct node_buffer_struct {
node_buffer_list_t markable;
struct rb_ast_local_table_link *local_tables;
VALUE mark_hash;
+ // - id (sequence number)
+ // - token_type
+ // - text of token
+ // - location info
+ // Array, whose entry is array
+ VALUE tokens;
};
static void
@@ -1184,6 +1193,7 @@ rb_node_buffer_new(void)
init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size));
nb->local_tables = 0;
nb->mark_hash = Qnil;
+ nb->tokens = Qnil;
return nb;
}
@@ -1415,7 +1425,10 @@ rb_ast_update_references(rb_ast_t *ast)
void
rb_ast_mark(rb_ast_t *ast)
{
- if (ast->node_buffer) rb_gc_mark(ast->node_buffer->mark_hash);
+ if (ast->node_buffer) {
+ rb_gc_mark(ast->node_buffer->mark_hash);
+ rb_gc_mark(ast->node_buffer->tokens);
+ }
if (ast->body.compile_option) rb_gc_mark(ast->body.compile_option);
if (ast->node_buffer) {
node_buffer_t *nb = ast->node_buffer;
@@ -1474,3 +1487,15 @@ rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj)
}
rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue);
}
+
+VALUE
+rb_ast_tokens(rb_ast_t *ast)
+{
+ return ast->node_buffer->tokens;
+}
+
+void
+rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens)
+{
+ RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens);
+}
diff --git a/node.h b/node.h
index d97c333ab5..befb1328fb 100644
--- a/node.h
+++ b/node.h
@@ -125,6 +125,7 @@ enum node_type {
NODE_ARYPTN,
NODE_HSHPTN,
NODE_FNDPTN,
+ NODE_ERROR,
NODE_LAST
};
@@ -383,9 +384,9 @@ typedef struct RNode {
#define NEW_FALSE(loc) NEW_NODE(NODE_FALSE,0,0,0,loc)
#define NEW_ERRINFO(loc) NEW_NODE(NODE_ERRINFO,0,0,0,loc)
#define NEW_DEFINED(e,loc) NEW_NODE(NODE_DEFINED,e,0,0,loc)
-#define NEW_PREEXE(b,loc) NEW_SCOPE(b,loc)
#define NEW_POSTEXE(b,loc) NEW_NODE(NODE_POSTEXE,0,b,0,loc)
#define NEW_ATTRASGN(r,m,a,loc) NEW_NODE(NODE_ATTRASGN,r,m,a,loc)
+#define NEW_ERROR(loc) NEW_NODE(NODE_ERROR,0,0,0,loc)
#define NODE_SPECIAL_REQUIRED_KEYWORD ((NODE *)-1)
#define NODE_REQUIRED_KEYWORD_P(node) ((node)->nd_value == NODE_SPECIAL_REQUIRED_KEYWORD)
@@ -420,6 +421,8 @@ void rb_ast_dispose(rb_ast_t*);
void rb_ast_free(rb_ast_t*);
size_t rb_ast_memsize(const rb_ast_t*);
void rb_ast_add_mark_object(rb_ast_t*, VALUE);
+void rb_ast_set_tokens(rb_ast_t*, VALUE);
+VALUE rb_ast_tokens(rb_ast_t *ast);
NODE *rb_ast_newnode(rb_ast_t*, enum node_type type);
void rb_ast_delete_node(rb_ast_t*, NODE *n);
rb_ast_id_table_t *rb_ast_new_local_table(rb_ast_t*, int);
@@ -460,6 +463,7 @@ struct rb_args_info {
NODE *opt_args;
unsigned int no_kwarg: 1;
unsigned int ruby2_keywords: 1;
+ unsigned int forwarding: 1;
VALUE imemo;
};
diff --git a/numeric.c b/numeric.c
index 4f927f00fb..6dd1ff6d2f 100644
--- a/numeric.c
+++ b/numeric.c
@@ -449,7 +449,7 @@ static int
do_coerce(VALUE *x, VALUE *y, int err)
{
VALUE ary = rb_check_funcall(*y, id_coerce, 1, x);
- if (ary == Qundef) {
+ if (UNDEF_P(ary)) {
if (err) {
coerce_failed(*x, *y);
}
@@ -1055,7 +1055,7 @@ flo_to_s(VALUE flt)
{
enum {decimal_mant = DBL_MANT_DIG-DBL_DIG};
enum {float_dig = DBL_DIG+1};
- char buf[float_dig + (decimal_mant + CHAR_BIT - 1) / CHAR_BIT + 10];
+ char buf[float_dig + roomof(decimal_mant, CHAR_BIT) + 10];
double value = RFLOAT_VALUE(flt);
VALUE s;
char *p, *e;
@@ -1713,7 +1713,7 @@ flo_cmp(VALUE x, VALUE y)
b = RFLOAT_VALUE(y);
}
else {
- if (isinf(a) && (i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0)) != Qundef) {
+ if (isinf(a) && !UNDEF_P(i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0))) {
if (RTEST(i)) {
int j = rb_cmpint(i, x, y);
j = (a > 0.0) ? (j > 0 ? 0 : +1) : (j < 0 ? 0 : -1);
@@ -2372,11 +2372,7 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
static VALUE
rb_int_floor(VALUE num, int ndigits)
{
- VALUE f;
-
- if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
- f = int_pow(10, -ndigits);
+ VALUE f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
int neg = x < 0;
@@ -2385,21 +2381,19 @@ rb_int_floor(VALUE num, int ndigits)
if (neg) x = -x;
return LONG2NUM(x);
}
- if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ else {
+ bool neg = int_neg_p(num);
+ if (neg) num = rb_int_minus(rb_int_plus(rb_int_uminus(num), f), INT2FIX(1));
+ num = rb_int_mul(rb_int_div(num, f), f);
+ if (neg) num = rb_int_uminus(num);
+ return num;
}
- return rb_int_minus(num, rb_int_modulo(num, f));
}
static VALUE
rb_int_ceil(VALUE num, int ndigits)
{
- VALUE f;
-
- if (int_round_zero_p(num, ndigits))
- return INT2FIX(0);
- f = int_pow(10, -ndigits);
+ VALUE f = int_pow(10, -ndigits);
if (FIXNUM_P(num) && FIXNUM_P(f)) {
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
int neg = x < 0;
@@ -2409,11 +2403,16 @@ rb_int_ceil(VALUE num, int ndigits)
if (neg) x = -x;
return LONG2NUM(x);
}
- if (RB_FLOAT_TYPE_P(f)) {
- /* then int_pow overflow */
- return INT2FIX(0);
+ else {
+ bool neg = int_neg_p(num);
+ if (neg)
+ num = rb_int_uminus(num);
+ else
+ num = rb_int_plus(num, rb_int_minus(f, INT2FIX(1)));
+ num = rb_int_mul(rb_int_div(num, f), f);
+ if (neg) num = rb_int_uminus(num);
+ return num;
}
- return rb_int_plus(num, rb_int_minus(f, rb_int_modulo(num, f)));
}
VALUE
@@ -2858,7 +2857,7 @@ num_step_negative_p(VALUE num)
}
r = rb_check_funcall(num, '>', 1, &zero);
- if (r == Qundef) {
+ if (UNDEF_P(r)) {
coerce_failed(num, INT2FIX(0));
}
return !RTEST(r);
@@ -2876,11 +2875,11 @@ num_step_extract_args(int argc, const VALUE *argv, VALUE *to, VALUE *step, VALUE
keys[0] = id_to;
keys[1] = id_by;
rb_get_kwargs(hash, keys, 0, 2, values);
- if (values[0] != Qundef) {
+ if (!UNDEF_P(values[0])) {
if (argc > 0) rb_raise(rb_eArgError, "to is given twice");
*to = values[0];
}
- if (values[1] != Qundef) {
+ if (!UNDEF_P(values[1])) {
if (argc > 1) rb_raise(rb_eArgError, "step is given twice");
*by = values[1];
}
@@ -2893,7 +2892,7 @@ static int
num_step_check_fix_args(int argc, VALUE *to, VALUE *step, VALUE by, int fix_nil, int allow_zero_step)
{
int desc;
- if (by != Qundef) {
+ if (!UNDEF_P(by)) {
*step = by;
}
else {
@@ -3041,7 +3040,7 @@ num_step(int argc, VALUE *argv, VALUE from)
VALUE by = Qundef;
num_step_extract_args(argc, argv, &to, &step, &by);
- if (by != Qundef) {
+ if (!UNDEF_P(by)) {
step = by;
}
if (NIL_P(step)) {
@@ -3056,7 +3055,7 @@ num_step(int argc, VALUE *argv, VALUE from)
num_step_size, from, to, step, FALSE);
}
- return SIZED_ENUMERATOR(from, 2, ((VALUE [2]){to, step}), num_step_size);
+ return SIZED_ENUMERATOR_KW(from, 2, ((VALUE [2]){to, step}), num_step_size, FALSE);
}
desc = num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE);
@@ -4930,7 +4929,7 @@ rb_num_coerce_bit(VALUE x, VALUE y, ID func)
do_coerce(&args[1], &args[2], TRUE);
ret = rb_exec_recursive_paired(num_funcall_bit_1,
args[2], args[1], (VALUE)args);
- if (ret == Qundef) {
+ if (UNDEF_P(ret)) {
/* show the original object, not coerced object */
coerce_failed(x, y);
}
diff --git a/numeric.rb b/numeric.rb
index c2091465f8..f026679210 100644
--- a/numeric.rb
+++ b/numeric.rb
@@ -6,7 +6,17 @@ class Numeric
# Returns +true+ if +num+ is a real number (i.e. not Complex).
#
def real?
- return true
+ true
+ end
+
+ #
+ # call-seq:
+ # num.real -> self
+ #
+ # Returns self.
+ #
+ def real
+ self
end
#
@@ -19,7 +29,7 @@ class Numeric
# 1.integer? #=> true
#
def integer?
- return false
+ false
end
#
@@ -29,7 +39,7 @@ class Numeric
# Returns +true+ if +num+ is a finite number, otherwise returns +false+.
#
def finite?
- return true
+ true
end
#
@@ -40,8 +50,34 @@ class Numeric
# finite, <code>-Infinity</code>, or <code>+Infinity</code>.
#
def infinite?
- return nil
+ nil
+ end
+
+ #
+ # call-seq:
+ # num.imag -> 0
+ # num.imaginary -> 0
+ #
+ # Returns zero.
+ #
+ def imaginary
+ 0
+ end
+
+ alias imag imaginary
+
+ #
+ # call-seq:
+ # num.conj -> self
+ # num.conjugate -> self
+ #
+ # Returns self.
+ #
+ def conjugate
+ self
end
+
+ alias conj conjugate
end
class Integer
@@ -146,7 +182,7 @@ class Integer
#
# Since +int+ is already an Integer, this always returns +true+.
def integer?
- return true
+ true
end
alias magnitude abs
@@ -178,7 +214,7 @@ class Integer
#
# For example, <code>?a.ord</code> returns 97 both in 1.8 and 1.9.
def ord
- return self
+ self
end
#
@@ -208,7 +244,7 @@ class Integer
#
# #to_int is an alias for #to_i.
def to_i
- return self
+ self
end
# call-seq:
@@ -216,7 +252,7 @@ class Integer
#
# Since +int+ is already an Integer, returns +self+.
def to_int
- return self
+ self
end
# call-seq:
@@ -244,6 +280,26 @@ class Integer
def ceildiv(other)
-div(-other)
end
+
+ #
+ # call-seq:
+ # int.numerator -> self
+ #
+ # Returns self.
+ #
+ def numerator
+ self
+ end
+
+ #
+ # call-seq:
+ # int.denominator -> 1
+ #
+ # Returns 1.
+ #
+ def denominator
+ 1
+ end
end
# call-seq:
@@ -276,7 +332,7 @@ class Float
# Since +float+ is already a Float, returns +self+.
#
def to_f
- return self
+ self
end
#
diff --git a/object.c b/object.c
index eb54d84967..e1fc72c89f 100644
--- a/object.c
+++ b/object.c
@@ -33,12 +33,14 @@
#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/variable.h"
+#include "variable.h"
#include "probes.h"
#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
#include "ruby/assert.h"
#include "builtin.h"
+#include "shape.h"
/*!
* \addtogroup object
@@ -99,7 +101,8 @@ rb_obj_reveal(VALUE obj, VALUE klass)
VALUE
rb_obj_setup(VALUE obj, VALUE klass, VALUE type)
{
- RBASIC(obj)->flags = type;
+ VALUE ignored_flags = RUBY_FL_PROMOTED | RUBY_FL_SEEN_OBJ_ID;
+ RBASIC(obj)->flags = (type & ~ignored_flags) | (RBASIC(obj)->flags & ignored_flags);
RBASIC_SET_CLASS(obj, klass);
return obj;
}
@@ -123,7 +126,7 @@ rb_equal(VALUE obj1, VALUE obj2)
if (obj1 == obj2) return Qtrue;
result = rb_equal_opt(obj1, obj2);
- if (result == Qundef) {
+ if (UNDEF_P(result)) {
result = rb_funcall(obj1, id_eq, 1, obj2);
}
return RBOOL(RTEST(result));
@@ -134,12 +137,12 @@ rb_eql(VALUE obj1, VALUE obj2)
{
VALUE result;
- if (obj1 == obj2) return Qtrue;
+ if (obj1 == obj2) return TRUE;
result = rb_eql_opt(obj1, obj2);
- if (result == Qundef) {
+ if (UNDEF_P(result)) {
result = rb_funcall(obj1, id_eql, 1, obj2);
}
- return RBOOL(RTEST(result));
+ return RTEST(result);
}
/**
@@ -267,13 +270,61 @@ rb_obj_singleton_class(VALUE obj)
MJIT_FUNC_EXPORTED void
rb_obj_copy_ivar(VALUE dest, VALUE obj)
{
- VALUE *dest_buf = ROBJECT_IVPTR(dest);
- VALUE *src_buf = ROBJECT_IVPTR(obj);
- uint32_t dest_len = ROBJECT_NUMIV(dest);
- uint32_t src_len = ROBJECT_NUMIV(obj);
- uint32_t len = dest_len < src_len ? dest_len : src_len;
+ RUBY_ASSERT(!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE));
+
+ RUBY_ASSERT(BUILTIN_TYPE(dest) == BUILTIN_TYPE(obj));
+ rb_shape_t * src_shape = rb_shape_get_shape(obj);
+
+ if (rb_shape_id(src_shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ st_table *table = st_copy(ROBJECT_IV_HASH(obj));
+
+ rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
+ rb_shape_set_too_complex(dest);
- MEMCPY(dest_buf, src_buf, VALUE, len);
+ ROBJECT(dest)->as.heap.ivptr = (VALUE *)table;
+
+ return;
+ }
+
+ uint32_t src_num_ivs = RBASIC_IV_COUNT(obj);
+ rb_shape_t * shape_to_set_on_dest = src_shape;
+ VALUE * src_buf;
+ VALUE * dest_buf;
+
+ if (!src_num_ivs) {
+ return;
+ }
+
+ // The copy should be mutable, so we don't want the frozen shape
+ if (rb_shape_frozen_shape_p(src_shape)) {
+ shape_to_set_on_dest = rb_shape_get_parent(src_shape);
+ }
+
+ src_buf = ROBJECT_IVPTR(obj);
+ dest_buf = ROBJECT_IVPTR(dest);
+
+ rb_shape_t * initial_shape = rb_shape_get_shape(dest);
+
+ if (initial_shape->size_pool_index != src_shape->size_pool_index) {
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+
+ shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
+ }
+
+ RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity);
+ if (initial_shape->capacity < shape_to_set_on_dest->capacity) {
+ rb_ensure_iv_list_size(dest, initial_shape->capacity, shape_to_set_on_dest->capacity);
+ dest_buf = ROBJECT_IVPTR(dest);
+ }
+
+ MEMCPY(dest_buf, src_buf, VALUE, src_num_ivs);
+
+ // Fire write barriers
+ for (uint32_t i = 0; i < src_num_ivs; i++) {
+ RB_OBJ_WRITTEN(dest, Qundef, dest_buf[i]);
+ }
+
+ rb_shape_set_shape(dest, shape_to_set_on_dest);
}
static void
@@ -283,10 +334,12 @@ init_copy(VALUE dest, VALUE obj)
rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest));
}
RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR);
+ // Copies the shape id from obj to dest
RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR);
rb_copy_wb_protected_attribute(dest, obj);
rb_copy_generic_ivar(dest, obj);
rb_gc_copy_finalizer(dest, obj);
+
if (RB_TYPE_P(obj, T_OBJECT)) {
rb_obj_copy_ivar(dest, obj);
}
@@ -357,7 +410,7 @@ rb_get_freeze_opt(int argc, VALUE *argv)
rb_scan_args(argc, argv, "0:", &opt);
if (!NIL_P(opt)) {
rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze);
- if (kwfreeze != Qundef)
+ if (!UNDEF_P(kwfreeze))
kwfreeze = obj_freeze_opt(kwfreeze);
}
return kwfreeze;
@@ -392,6 +445,9 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
case Qnil:
rb_funcall(clone, id_init_clone, 1, obj);
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
+ if (RB_OBJ_FROZEN(obj)) {
+ rb_shape_transition_shape_frozen(clone);
+ }
break;
case Qtrue:
{
@@ -407,6 +463,7 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
argv[1] = freeze_true_hash;
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
RBASIC(clone)->flags |= FL_FREEZE;
+ rb_shape_transition_shape_frozen(clone);
break;
}
case Qfalse:
@@ -633,8 +690,8 @@ inspect_i(st_data_t k, st_data_t v, st_data_t a)
else {
rb_str_cat2(str, ", ");
}
- rb_str_catf(str, "%"PRIsVALUE"=%+"PRIsVALUE,
- rb_id2str(id), value);
+ rb_str_catf(str, "%"PRIsVALUE"=", rb_id2str(id));
+ rb_str_buf_append(str, rb_inspect(value));
return ST_CONTINUE;
}
@@ -2023,6 +2080,10 @@ rb_class_superclass(VALUE klass)
if (klass == rb_cBasicObject) return Qnil;
rb_raise(rb_eTypeError, "uninitialized class");
}
+
+ if (!RCLASS_SUPERCLASS_DEPTH(klass)) {
+ return Qnil;
+ }
else {
super = RCLASS_SUPERCLASSES(klass)[RCLASS_SUPERCLASS_DEPTH(klass) - 1];
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
@@ -2487,7 +2548,7 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
#if 0
mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
- if (mod == Qundef) return Qfalse;
+ if (UNDEF_P(mod)) return Qfalse;
#else
if (!RTEST(recur)) {
if (!rb_const_defined_at(mod, id))
@@ -2732,7 +2793,7 @@ rb_obj_ivar_get(VALUE obj, VALUE iv)
*/
static VALUE
-rb_obj_ivar_set(VALUE obj, VALUE iv, VALUE val)
+rb_obj_ivar_set_m(VALUE obj, VALUE iv, VALUE val)
{
ID id = id_for_var(obj, iv, instance);
if (!id) id = rb_intern_str(iv);
@@ -2917,7 +2978,7 @@ static VALUE
convert_type_with_id(VALUE val, const char *tname, ID method, int raise, int index)
{
VALUE r = rb_check_funcall(val, method, 0, 0);
- if (r == Qundef) {
+ if (UNDEF_P(r)) {
if (raise) {
const char *msg =
((index < 0 ? conv_method_index(rb_id2name(method)) : index)
@@ -3112,6 +3173,9 @@ rb_convert_to_integer(VALUE val, int base, int raise_exception)
tmp = rb_protect(rb_check_to_int, val, NULL);
if (RB_INTEGER_TYPE_P(tmp)) return tmp;
rb_set_errinfo(Qnil);
+ if (!NIL_P(tmp = rb_check_string_type(val))) {
+ return rb_str_convert_to_inum(tmp, base, TRUE, raise_exception);
+ }
if (!raise_exception) {
VALUE result = rb_protect(rb_check_to_i, val, NULL);
@@ -3175,6 +3239,11 @@ rb_opts_exception_p(VALUE opts, int default_value)
* using +to_int+ first and +to_i+ second;
* see below for exceptions.
*
+ * With a non-zero +base+, +object+ must be a string or convertible
+ * to a string.
+ *
+ * ==== numeric objects
+ *
* With integer argument +object+ given, returns +object+:
*
* Integer(1) # => 1
@@ -3186,6 +3255,8 @@ rb_opts_exception_p(VALUE opts, int default_value)
* Integer(1.9) # => 1 # Rounds toward zero.
* Integer(-1.9) # => -1 # Rounds toward zero.
*
+ * ==== string objects
+ *
* With string argument +object+ and zero +base+ given,
* returns +object+ converted to an integer in base 10:
*
@@ -3193,32 +3264,48 @@ rb_opts_exception_p(VALUE opts, int default_value)
* Integer('-100') # => -100
*
* With +base+ zero, string +object+ may contain leading characters
- * to specify the actual base:
+ * to specify the actual base (radix indicator):
*
* Integer('0100') # => 64 # Leading '0' specifies base 8.
* Integer('0b100') # => 4 # Leading '0b', specifies base 2.
* Integer('0x100') # => 256 # Leading '0x' specifies base 16.
*
- * With a non-zero +base+ (in range 2..36) given
- * (in which case +object+ must be a string),
- * returns +object+ converted to an integer in the given base:
+ * With a positive +base+ (in range 2..36) given, returns +object+
+ * converted to an integer in the given base:
*
* Integer('100', 2) # => 4
* Integer('100', 8) # => 64
* Integer('-100', 16) # => -256
*
+ * With a negative +base+ (in range -36..-2) given, returns +object+
+ * converted to an integer in the radix indicator if exists or
+ * +-base+:
+ *
+ * Integer('0x100', -2) # => 256
+ * Integer('100', -2) # => 4
+ * Integer('0b100', -8) # => 4
+ * Integer('100', -8) # => 64
+ * Integer('0o100', -10) # => 64
+ * Integer('100', -10) # => 100
+ *
+ * +base+ -1 is equal the -10 case.
+ *
* When converting strings, surrounding whitespace and embedded underscores
* are allowed and ignored:
*
* Integer(' 100 ') # => 100
* Integer('-1_0_0', 16) # => -256
*
+ * ==== other classes
+ *
* Examples with +object+ of various other classes:
*
* Integer(Rational(9, 10)) # => 0 # Rounds toward zero.
* Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero.
* Integer(Time.now) # => 1650974042
*
+ * ==== keywords
+ *
* With optional keyword argument +exception+ given as +true+ (the default):
*
* - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+.
@@ -3388,6 +3475,7 @@ rb_str_to_dbl_raise(VALUE str, int badcheck, int raise, int *error)
VALUE v = 0;
StringValue(str);
+ rb_must_asciicompat(str);
s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
if (s) {
@@ -4315,7 +4403,7 @@ InitVM_Object(void)
rb_define_method(rb_mKernel, "public_methods", rb_obj_public_methods, -1); /* in class.c */
rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0); /* in variable.c */
rb_define_method(rb_mKernel, "instance_variable_get", rb_obj_ivar_get, 1);
- rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set, 2);
+ rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set_m, 2);
rb_define_method(rb_mKernel, "instance_variable_defined?", rb_obj_ivar_defined, 1);
rb_define_method(rb_mKernel, "remove_instance_variable",
rb_obj_remove_instance_variable, 1); /* in variable.c */
@@ -4412,6 +4500,7 @@ InitVM_Object(void)
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
rb_define_method(rb_cClass, "subclasses", rb_class_subclasses, 0); /* in class.c */
+ rb_define_method(rb_cClass, "attached_object", rb_class_attached_object, 0); /* in class.c */
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
rb_undef_method(rb_cClass, "extend_object");
rb_undef_method(rb_cClass, "append_features");
diff --git a/pack.c b/pack.c
index 2817491b77..3bdae01362 100644
--- a/pack.c
+++ b/pack.c
@@ -167,8 +167,8 @@ unknown_directive(const char *mode, char type, VALUE fmt)
snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
}
fmt = rb_str_quote_unprintable(fmt);
- rb_warning("unknown %s directive '%s' in '%"PRIsVALUE"'",
- mode, unknown, fmt);
+ rb_warn("unknown %s directive '%s' in '%"PRIsVALUE"'",
+ mode, unknown, fmt);
}
static float
@@ -208,6 +208,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
int integer_size, bigendian_p;
StringValue(fmt);
+ rb_must_asciicompat(fmt);
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
@@ -217,6 +218,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
else {
if (!RB_TYPE_P(buffer, T_STRING))
rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
+ rb_str_modify(buffer);
res = buffer;
}
@@ -476,40 +478,24 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
goto pack_integer;
case 's': /* s for int16_t, s! for signed short */
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
case 'S': /* S for uint16_t, S! for unsigned short */
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'i': /* i and i! for signed int */
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
case 'I': /* I and I! for unsigned int */
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'l': /* l for int32_t, l! for signed long */
- integer_size = NATINT_LEN(long, 4);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
case 'L': /* L for uint32_t, L! for unsigned long */
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'q': /* q for int64_t, q! for signed long long */
- integer_size = NATINT_LEN_Q;
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
case 'Q': /* Q for uint64_t, Q! for unsigned long long */
integer_size = NATINT_LEN_Q;
bigendian_p = BIGENDIAN_P();
@@ -938,13 +924,14 @@ hex2num(char c)
# define AVOID_CC_BUG
#endif
-/* unpack mode */
-#define UNPACK_ARRAY 0
-#define UNPACK_BLOCK 1
-#define UNPACK_1 2
+enum unpack_mode {
+ UNPACK_ARRAY,
+ UNPACK_BLOCK,
+ UNPACK_1
+};
static VALUE
-pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
+pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
{
#define hexdigits ruby_hexdigits
char *s, *send;
@@ -973,6 +960,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
StringValue(str);
StringValue(fmt);
+ rb_must_asciicompat(fmt);
if (offset < 0) rb_raise(rb_eArgError, "offset can't be negative");
len = RSTRING_LEN(str);
@@ -1623,7 +1611,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode, long offset)
static VALUE
pack_unpack(rb_execution_context_t *ec, VALUE str, VALUE fmt, VALUE offset)
{
- int mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
+ enum unpack_mode mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
return pack_unpack_internal(str, fmt, mode, RB_NUM2LONG(offset));
}
diff --git a/pack.rb b/pack.rb
index 0f5b75ba43..d505eaee35 100644
--- a/pack.rb
+++ b/pack.rb
@@ -1,143 +1,9 @@
class Array
# call-seq:
- # arr.pack( aTemplateString ) -> aBinaryString
- # arr.pack( aTemplateString, buffer: aBufferString ) -> aBufferString
+ # pack(template, buffer: nil) -> string
#
- # Packs the contents of <i>arr</i> into a binary sequence according to
- # the directives in <i>aTemplateString</i> (see the table below)
- # Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
- # which gives the width of the resulting field. The remaining
- # directives also may take a count, indicating the number of array
- # elements to convert. If the count is an asterisk
- # (``<code>*</code>''), all remaining array elements will be
- # converted. Any of the directives ``<code>sSiIlL</code>'' may be
- # followed by an underscore (``<code>_</code>'') or
- # exclamation mark (``<code>!</code>'') to use the underlying
- # platform's native size for the specified type; otherwise, they use a
- # platform-independent size. Spaces are ignored in the template
- # string. See also String#unpack.
- #
- # a = [ "a", "b", "c" ]
- # n = [ 65, 66, 67 ]
- # a.pack("A3A3A3") #=> "a b c "
- # a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
- # n.pack("ccc") #=> "ABC"
- #
- # If <i>aBufferString</i> is specified and its capacity is enough,
- # +pack+ uses it as the buffer and returns it.
- # When the offset is specified by the beginning of <i>aTemplateString</i>,
- # the result is filled after the offset.
- # If original contents of <i>aBufferString</i> exists and it's longer than
- # the offset, the rest of <i>offsetOfBuffer</i> are overwritten by the result.
- # If it's shorter, the gap is filled with ``<code>\0</code>''.
- #
- # # packed data is appended by default
- # [255].pack("C", buffer:"foo".b) #=> "foo\xFF"
- #
- # # "@0" (offset 0) specifies that packed data is filled from beginning.
- # # Also, original data after packed data is removed. ("oo" is removed.)
- # [255].pack("@0C", buffer:"foo".b) #=> "\xFF"
- #
- # # If the offset is bigger than the original length, \x00 is filled.
- # [255].pack("@5C", buffer:"foo".b) #=> "foo\x00\x00\xFF"
- #
- # Note that ``buffer:'' option does not guarantee not to allocate memory
- # in +pack+. If the capacity of <i>aBufferString</i> is not enough,
- # +pack+ allocates memory.
- #
- # Directives for +pack+.
- #
- # Integer | Array |
- # Directive | Element | Meaning
- # ----------------------------------------------------------------------------
- # C | Integer | 8-bit unsigned (unsigned char)
- # S | Integer | 16-bit unsigned, native endian (uint16_t)
- # L | Integer | 32-bit unsigned, native endian (uint32_t)
- # Q | Integer | 64-bit unsigned, native endian (uint64_t)
- # J | Integer | pointer width unsigned, native endian (uintptr_t)
- # | | (J is available since Ruby 2.3.)
- # | |
- # c | Integer | 8-bit signed (signed char)
- # s | Integer | 16-bit signed, native endian (int16_t)
- # l | Integer | 32-bit signed, native endian (int32_t)
- # q | Integer | 64-bit signed, native endian (int64_t)
- # j | Integer | pointer width signed, native endian (intptr_t)
- # | | (j is available since Ruby 2.3.)
- # | |
- # S_ S! | Integer | unsigned short, native endian
- # I I_ I! | Integer | unsigned int, native endian
- # L_ L! | Integer | unsigned long, native endian
- # Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # | | (Q_ and Q! is available since Ruby 2.1.)
- # J! | Integer | uintptr_t, native endian (same with J)
- # | | (J! is available since Ruby 2.3.)
- # | |
- # s_ s! | Integer | signed short, native endian
- # i i_ i! | Integer | signed int, native endian
- # l_ l! | Integer | signed long, native endian
- # q_ q! | Integer | signed long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # | | (q_ and q! is available since Ruby 2.1.)
- # j! | Integer | intptr_t, native endian (same with j)
- # | | (j! is available since Ruby 2.3.)
- # | |
- # S> s> S!> s!> | Integer | same as the directives without ">" except
- # L> l> L!> l!> | | big endian
- # I!> i!> | | (available since Ruby 1.9.3)
- # Q> q> Q!> q!> | | "S>" is the same as "n"
- # J> j> J!> j!> | | "L>" is the same as "N"
- # | |
- # S< s< S!< s!< | Integer | same as the directives without "<" except
- # L< l< L!< l!< | | little endian
- # I!< i!< | | (available since Ruby 1.9.3)
- # Q< q< Q!< q!< | | "S<" is the same as "v"
- # J< j< J!< j!< | | "L<" is the same as "V"
- # | |
- # n | Integer | 16-bit unsigned, network (big-endian) byte order
- # N | Integer | 32-bit unsigned, network (big-endian) byte order
- # v | Integer | 16-bit unsigned, VAX (little-endian) byte order
- # V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- # | |
- # U | Integer | UTF-8 character
- # w | Integer | BER-compressed integer
- #
- # Float | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # D d | Float | double-precision, native format
- # F f | Float | single-precision, native format
- # E | Float | double-precision, little-endian byte order
- # e | Float | single-precision, little-endian byte order
- # G | Float | double-precision, network (big-endian) byte order
- # g | Float | single-precision, network (big-endian) byte order
- #
- # String | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # A | String | arbitrary binary string (space padded, count is width)
- # a | String | arbitrary binary string (null padded, count is width)
- # Z | String | same as ``a'', except that null is added with *
- # B | String | bit string (MSB first)
- # b | String | bit string (LSB first)
- # H | String | hex string (high nibble first)
- # h | String | hex string (low nibble first)
- # u | String | UU-encoded string
- # M | String | quoted printable, MIME encoding (see also RFC2045)
- # | | (text mode but input must use LF and output LF)
- # m | String | base64 encoded string (see RFC 2045)
- # | | (if count is 0, no line feed are added, see RFC 4648)
- # | | (count specifies input bytes between each LF,
- # | | rounded down to nearest multiple of 3)
- # P | String | pointer to a structure (fixed-length string)
- # p | String | pointer to a null-terminated string
- #
- # Misc. | Array |
- # Directive | Element | Meaning
- # ---------------------------------------------------------------------------
- # @ | --- | moves to absolute position
- # X | --- | back up a byte
- # x | --- | null byte
+ # Formats each element in +self+ into a binary string; returns that string.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def pack(fmt, buffer: nil)
Primitive.pack_pack(fmt, buffer)
end
@@ -145,164 +11,20 @@ end
class String
# call-seq:
- # str.unpack(format) -> anArray
- # str.unpack(format, offset: anInteger) -> anArray
- #
- # Decodes <i>str</i> (which may contain binary data) according to the
- # format string, returning an array of each value extracted.
- # The format string consists of a sequence of single-character directives,
- # summarized in the table at the end of this entry.
- # Each directive may be followed
- # by a number, indicating the number of times to repeat with this
- # directive. An asterisk (``<code>*</code>'') will use up all
- # remaining elements. The directives <code>sSiIlL</code> may each be
- # followed by an underscore (``<code>_</code>'') or
- # exclamation mark (``<code>!</code>'') to use the underlying
- # platform's native size for the specified type; otherwise, it uses a
- # platform-independent consistent size. Spaces are ignored in the
- # format string.
- #
- # See also String#unpack1, Array#pack.
- #
- # "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
- # "abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
- # "abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
- # "aa".unpack('b8B8') #=> ["10000110", "01100001"]
- # "aaa".unpack('h2H2c') #=> ["16", "61", 97]
- # "\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
- # "now=20is".unpack('M*') #=> ["now is"]
- # "whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
- #
- # This table summarizes the various formats and the Ruby classes
- # returned by each.
+ # unpack(template, offset: 0) -> array
#
- # Integer | |
- # Directive | Returns | Meaning
- # ------------------------------------------------------------------
- # C | Integer | 8-bit unsigned (unsigned char)
- # S | Integer | 16-bit unsigned, native endian (uint16_t)
- # L | Integer | 32-bit unsigned, native endian (uint32_t)
- # Q | Integer | 64-bit unsigned, native endian (uint64_t)
- # J | Integer | pointer width unsigned, native endian (uintptr_t)
- # | |
- # c | Integer | 8-bit signed (signed char)
- # s | Integer | 16-bit signed, native endian (int16_t)
- # l | Integer | 32-bit signed, native endian (int32_t)
- # q | Integer | 64-bit signed, native endian (int64_t)
- # j | Integer | pointer width signed, native endian (intptr_t)
- # | |
- # S_ S! | Integer | unsigned short, native endian
- # I I_ I! | Integer | unsigned int, native endian
- # L_ L! | Integer | unsigned long, native endian
- # Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # J! | Integer | uintptr_t, native endian (same with J)
- # | |
- # s_ s! | Integer | signed short, native endian
- # i i_ i! | Integer | signed int, native endian
- # l_ l! | Integer | signed long, native endian
- # q_ q! | Integer | signed long long, native endian (ArgumentError
- # | | if the platform has no long long type.)
- # j! | Integer | intptr_t, native endian (same with j)
- # | |
- # S> s> S!> s!> | Integer | same as the directives without ">" except
- # L> l> L!> l!> | | big endian
- # I!> i!> | |
- # Q> q> Q!> q!> | | "S>" is the same as "n"
- # J> j> J!> j!> | | "L>" is the same as "N"
- # | |
- # S< s< S!< s!< | Integer | same as the directives without "<" except
- # L< l< L!< l!< | | little endian
- # I!< i!< | |
- # Q< q< Q!< q!< | | "S<" is the same as "v"
- # J< j< J!< j!< | | "L<" is the same as "V"
- # | |
- # n | Integer | 16-bit unsigned, network (big-endian) byte order
- # N | Integer | 32-bit unsigned, network (big-endian) byte order
- # v | Integer | 16-bit unsigned, VAX (little-endian) byte order
- # V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- # | |
- # U | Integer | UTF-8 character
- # w | Integer | BER-compressed integer (see Array#pack)
- #
- # Float | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # D d | Float | double-precision, native format
- # F f | Float | single-precision, native format
- # E | Float | double-precision, little-endian byte order
- # e | Float | single-precision, little-endian byte order
- # G | Float | double-precision, network (big-endian) byte order
- # g | Float | single-precision, network (big-endian) byte order
- #
- # String | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # A | String | arbitrary binary string (remove trailing nulls and ASCII spaces)
- # a | String | arbitrary binary string
- # Z | String | null-terminated string
- # B | String | bit string (MSB first)
- # b | String | bit string (LSB first)
- # H | String | hex string (high nibble first)
- # h | String | hex string (low nibble first)
- # u | String | UU-encoded string
- # M | String | quoted-printable, MIME encoding (see RFC2045)
- # m | String | base64 encoded string (RFC 2045) (default)
- # | | base64 encoded string (RFC 4648) if followed by 0
- # P | String | pointer to a structure (fixed-length string)
- # p | String | pointer to a null-terminated string
- #
- # Misc. | |
- # Directive | Returns | Meaning
- # -----------------------------------------------------------------
- # @ | --- | skip to the offset given by the length argument
- # X | --- | skip backward one byte
- # x | --- | skip forward one byte
- #
- # The keyword <i>offset</i> can be given to start the decoding after skipping
- # the specified amount of bytes:
- # "abc".unpack("C*") # => [97, 98, 99]
- # "abc".unpack("C*", offset: 2) # => [99]
- # "abc".unpack("C*", offset: 4) # => offset outside of string (ArgumentError)
- #
- # HISTORY
- #
- # * J, J! j, and j! are available since Ruby 2.3.
- # * Q_, Q!, q_, and q! are available since Ruby 2.1.
- # * I!<, i!<, I!>, and i!> are available since Ruby 1.9.3.
+ # Extracts data from +self+, forming objects that become the elements of a new array;
+ # returns that array.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def unpack(fmt, offset: 0)
Primitive.pack_unpack(fmt, offset)
end
# call-seq:
- # str.unpack1(format) -> obj
- # str.unpack1(format, offset: anInteger) -> obj
- #
- # Decodes <i>str</i> (which may contain binary data) according to the
- # format string, returning the first value extracted.
- #
- # See also String#unpack, Array#pack.
- #
- # Contrast with String#unpack:
- #
- # "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
- # "abc \0\0abc \0\0".unpack1('A6Z6') #=> "abc"
- #
- # In that case data would be lost but often it's the case that the array
- # only holds one value, especially when unpacking binary data. For instance:
- #
- # "\xff\x00\x00\x00".unpack("l") #=> [255]
- # "\xff\x00\x00\x00".unpack1("l") #=> 255
- #
- # Thus unpack1 is convenient, makes clear the intention and signals
- # the expected return value to those reading the code.
- #
- # The keyword <i>offset</i> can be given to start the decoding after skipping
- # the specified amount of bytes:
- # "abc".unpack1("C*") # => 97
- # "abc".unpack1("C*", offset: 2) # => 99
- # "abc".unpack1("C*", offset: 4) # => offset outside of string (ArgumentError)
+ # unpack1(template, offset: 0) -> object
#
+ # Like String#unpack, but unpacks and returns only the first extracted object.
+ # See {Packed Data}[rdoc-ref:packed_data.rdoc].
def unpack1(fmt, offset: 0)
Primitive.pack_unpack1(fmt, offset)
end
diff --git a/parse.y b/parse.y
index 405a83a8cf..1c808bd60e 100644
--- a/parse.y
+++ b/parse.y
@@ -9,6 +9,8 @@
**********************************************************************/
+%require "3.0"
+
%{
#if !YYPURE
@@ -32,6 +34,7 @@ struct lex_context;
#include "internal/compile.h"
#include "internal/compilers.h"
#include "internal/complex.h"
+#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/hash.h"
#include "internal/imemo.h"
@@ -70,7 +73,7 @@ struct lex_context {
BITFIELD(enum shareability, shareable_constant_value, 2);
};
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(__clang__)
// Suppress "parameter passing for argument of type 'struct
// lex_context' changed" notes. `struct lex_context` is file scope,
// and has no ABI compatibility issue.
@@ -122,7 +125,13 @@ RBIMPL_WARNING_POP()
#define RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(Current) \
rb_parser_set_location_from_strterm_heredoc(p, &p->lex.strterm->u.heredoc, &(Current))
-#define RUBY_SET_YYLLOC_OF_NONE(Current) \
+#define RUBY_SET_YYLLOC_OF_DELAYED_TOKEN(Current) \
+ rb_parser_set_location_of_delayed_token(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_HEREDOC_END(Current) \
+ rb_parser_set_location_of_heredoc_end(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_DUMMY_END(Current) \
+ rb_parser_set_location_of_dummy_end(p, &(Current))
+#define RUBY_SET_YYLLOC_OF_NONE(Current) \
rb_parser_set_location_of_none(p, &(Current))
#define RUBY_SET_YYLLOC(Current) \
rb_parser_set_location(p, &(Current))
@@ -270,12 +279,12 @@ struct parser_params {
rb_imemo_tmpbuf_t *heap;
YYSTYPE *lval;
+ YYLTYPE *yylloc;
struct {
rb_strterm_t *strterm;
VALUE (*gets)(struct parser_params*,VALUE);
VALUE input;
- VALUE prevline;
VALUE lastline;
VALUE nextline;
const char *pbeg;
@@ -318,6 +327,14 @@ struct parser_params {
VALUE debug_buffer;
VALUE debug_output;
+ struct {
+ VALUE token;
+ int beg_line;
+ int beg_col;
+ int end_line;
+ int end_col;
+ } delayed;
+
ID cur_arg;
rb_ast_t *ast;
@@ -348,21 +365,23 @@ struct parser_params {
unsigned int do_chomp: 1;
unsigned int do_split: 1;
unsigned int keep_script_lines: 1;
+ unsigned int error_tolerant: 1;
+ unsigned int keep_tokens: 1;
NODE *eval_tree_begin;
NODE *eval_tree;
VALUE error_buffer;
VALUE debug_lines;
const struct rb_iseq_struct *parent_iseq;
+ /* store specific keyword locations to generate dummy end token */
+ VALUE end_expect_token_locations;
+ /* id for terms */
+ int token_id;
+ /* Array for term tokens */
+ VALUE tokens;
#else
/* Ripper only */
- struct {
- VALUE token;
- int line;
- int col;
- } delayed;
-
VALUE value;
VALUE result;
VALUE parsing_thread;
@@ -407,6 +426,214 @@ pop_pktbl(struct parser_params *p, st_table *tbl)
p->pktbl = tbl;
}
+#ifndef RIPPER
+static void flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str);
+
+static void
+debug_end_expect_token_locations(struct parser_params *p, const char *name)
+{
+ if(p->debug) {
+ VALUE mesg = rb_sprintf("%s: ", name);
+ rb_str_catf(mesg, " %"PRIsVALUE"\n", p->end_expect_token_locations);
+ flush_debug_buffer(p, p->debug_output, mesg);
+ }
+}
+
+static void
+push_end_expect_token_locations(struct parser_params *p, const rb_code_position_t *pos)
+{
+ if(NIL_P(p->end_expect_token_locations)) return;
+ rb_ary_push(p->end_expect_token_locations, rb_ary_new_from_args(2, INT2NUM(pos->lineno), INT2NUM(pos->column)));
+ debug_end_expect_token_locations(p, "push_end_expect_token_locations");
+}
+
+static void
+pop_end_expect_token_locations(struct parser_params *p)
+{
+ if(NIL_P(p->end_expect_token_locations)) return;
+ rb_ary_pop(p->end_expect_token_locations);
+ debug_end_expect_token_locations(p, "pop_end_expect_token_locations");
+}
+
+static VALUE
+peek_end_expect_token_locations(struct parser_params *p)
+{
+ if(NIL_P(p->end_expect_token_locations)) return Qnil;
+ return rb_ary_last(0, 0, p->end_expect_token_locations);
+}
+
+static ID
+parser_token2id(enum yytokentype tok)
+{
+ switch ((int) tok) {
+#define TOKEN2ID(tok) case tok: return rb_intern(#tok);
+#define TOKEN2ID2(tok, name) case tok: return rb_intern(name);
+ TOKEN2ID2(' ', "words_sep")
+ TOKEN2ID2('!', "!")
+ TOKEN2ID2('%', "%");
+ TOKEN2ID2('&', "&");
+ TOKEN2ID2('*', "*");
+ TOKEN2ID2('+', "+");
+ TOKEN2ID2('-', "-");
+ TOKEN2ID2('/', "/");
+ TOKEN2ID2('<', "<");
+ TOKEN2ID2('=', "=");
+ TOKEN2ID2('>', ">");
+ TOKEN2ID2('?', "?");
+ TOKEN2ID2('^', "^");
+ TOKEN2ID2('|', "|");
+ TOKEN2ID2('~', "~");
+ TOKEN2ID2(':', ":");
+ TOKEN2ID2(',', ",");
+ TOKEN2ID2('.', ".");
+ TOKEN2ID2(';', ";");
+ TOKEN2ID2('`', "`");
+ TOKEN2ID2('\n', "nl");
+ TOKEN2ID2('{', "{");
+ TOKEN2ID2('}', "}");
+ TOKEN2ID2('[', "[");
+ TOKEN2ID2(']', "]");
+ TOKEN2ID2('(', "(");
+ TOKEN2ID2(')', ")");
+ TOKEN2ID(keyword_class);
+ TOKEN2ID(keyword_module);
+ TOKEN2ID(keyword_def);
+ TOKEN2ID(keyword_undef);
+ TOKEN2ID(keyword_begin);
+ TOKEN2ID(keyword_rescue);
+ TOKEN2ID(keyword_ensure);
+ TOKEN2ID(keyword_end);
+ TOKEN2ID(keyword_if);
+ TOKEN2ID(keyword_unless);
+ TOKEN2ID(keyword_then);
+ TOKEN2ID(keyword_elsif);
+ TOKEN2ID(keyword_else);
+ TOKEN2ID(keyword_case);
+ TOKEN2ID(keyword_when);
+ TOKEN2ID(keyword_while);
+ TOKEN2ID(keyword_until);
+ TOKEN2ID(keyword_for);
+ TOKEN2ID(keyword_break);
+ TOKEN2ID(keyword_next);
+ TOKEN2ID(keyword_redo);
+ TOKEN2ID(keyword_retry);
+ TOKEN2ID(keyword_in);
+ TOKEN2ID(keyword_do);
+ TOKEN2ID(keyword_do_cond);
+ TOKEN2ID(keyword_do_block);
+ TOKEN2ID(keyword_do_LAMBDA);
+ TOKEN2ID(keyword_return);
+ TOKEN2ID(keyword_yield);
+ TOKEN2ID(keyword_super);
+ TOKEN2ID(keyword_self);
+ TOKEN2ID(keyword_nil);
+ TOKEN2ID(keyword_true);
+ TOKEN2ID(keyword_false);
+ TOKEN2ID(keyword_and);
+ TOKEN2ID(keyword_or);
+ TOKEN2ID(keyword_not);
+ TOKEN2ID(modifier_if);
+ TOKEN2ID(modifier_unless);
+ TOKEN2ID(modifier_while);
+ TOKEN2ID(modifier_until);
+ TOKEN2ID(modifier_rescue);
+ TOKEN2ID(keyword_alias);
+ TOKEN2ID(keyword_defined);
+ TOKEN2ID(keyword_BEGIN);
+ TOKEN2ID(keyword_END);
+ TOKEN2ID(keyword__LINE__);
+ TOKEN2ID(keyword__FILE__);
+ TOKEN2ID(keyword__ENCODING__);
+ TOKEN2ID(tIDENTIFIER);
+ TOKEN2ID(tFID);
+ TOKEN2ID(tGVAR);
+ TOKEN2ID(tIVAR);
+ TOKEN2ID(tCONSTANT);
+ TOKEN2ID(tCVAR);
+ TOKEN2ID(tLABEL);
+ TOKEN2ID(tINTEGER);
+ TOKEN2ID(tFLOAT);
+ TOKEN2ID(tRATIONAL);
+ TOKEN2ID(tIMAGINARY);
+ TOKEN2ID(tCHAR);
+ TOKEN2ID(tNTH_REF);
+ TOKEN2ID(tBACK_REF);
+ TOKEN2ID(tSTRING_CONTENT);
+ TOKEN2ID(tREGEXP_END);
+ TOKEN2ID(tDUMNY_END);
+ TOKEN2ID(tSP);
+ TOKEN2ID(tUPLUS);
+ TOKEN2ID(tUMINUS);
+ TOKEN2ID(tPOW);
+ TOKEN2ID(tCMP);
+ TOKEN2ID(tEQ);
+ TOKEN2ID(tEQQ);
+ TOKEN2ID(tNEQ);
+ TOKEN2ID(tGEQ);
+ TOKEN2ID(tLEQ);
+ TOKEN2ID(tANDOP);
+ TOKEN2ID(tOROP);
+ TOKEN2ID(tMATCH);
+ TOKEN2ID(tNMATCH);
+ TOKEN2ID(tDOT2);
+ TOKEN2ID(tDOT3);
+ TOKEN2ID(tBDOT2);
+ TOKEN2ID(tBDOT3);
+ TOKEN2ID(tAREF);
+ TOKEN2ID(tASET);
+ TOKEN2ID(tLSHFT);
+ TOKEN2ID(tRSHFT);
+ TOKEN2ID(tANDDOT);
+ TOKEN2ID(tCOLON2);
+ TOKEN2ID(tCOLON3);
+ TOKEN2ID(tOP_ASGN);
+ TOKEN2ID(tASSOC);
+ TOKEN2ID(tLPAREN);
+ TOKEN2ID(tLPAREN_ARG);
+ TOKEN2ID(tRPAREN);
+ TOKEN2ID(tLBRACK);
+ TOKEN2ID(tLBRACE);
+ TOKEN2ID(tLBRACE_ARG);
+ TOKEN2ID(tSTAR);
+ TOKEN2ID(tDSTAR);
+ TOKEN2ID(tAMPER);
+ TOKEN2ID(tLAMBDA);
+ TOKEN2ID(tSYMBEG);
+ TOKEN2ID(tSTRING_BEG);
+ TOKEN2ID(tXSTRING_BEG);
+ TOKEN2ID(tREGEXP_BEG);
+ TOKEN2ID(tWORDS_BEG);
+ TOKEN2ID(tQWORDS_BEG);
+ TOKEN2ID(tSYMBOLS_BEG);
+ TOKEN2ID(tQSYMBOLS_BEG);
+ TOKEN2ID(tSTRING_END);
+ TOKEN2ID(tSTRING_DEND);
+ TOKEN2ID(tSTRING_DBEG);
+ TOKEN2ID(tSTRING_DVAR);
+ TOKEN2ID(tLAMBEG);
+ TOKEN2ID(tLABEL_END);
+ TOKEN2ID(tIGNORED_NL);
+ TOKEN2ID(tCOMMENT);
+ TOKEN2ID(tEMBDOC_BEG);
+ TOKEN2ID(tEMBDOC);
+ TOKEN2ID(tEMBDOC_END);
+ TOKEN2ID(tHEREDOC_BEG);
+ TOKEN2ID(tHEREDOC_END);
+ TOKEN2ID(k__END__);
+ TOKEN2ID(tLOWEST);
+ TOKEN2ID(tUMINUS_NUM);
+ TOKEN2ID(tLAST_TOKEN);
+#undef TOKEN2ID
+#undef TOKEN2ID2
+ }
+
+ rb_bug("parser_token2id: unknown token %d", tok);
+
+ UNREACHABLE_RETURN(0);
+}
+
+#endif
+
RBIMPL_ATTR_NONNULL((1, 2, 3))
static int parser_yyerror(struct parser_params*, const YYLTYPE *yylloc, const char*);
RBIMPL_ATTR_NONNULL((1, 2))
@@ -415,6 +642,9 @@ static int parser_yyerror0(struct parser_params*, const char*);
#define yyerror1(loc, msg) parser_yyerror(p, (loc), (msg))
#define yyerror(yylloc, p, msg) parser_yyerror(p, yylloc, msg)
#define token_flush(ptr) ((ptr)->lex.ptok = (ptr)->lex.pcur)
+#define lex_goto_eol(p) ((p)->lex.pcur = (p)->lex.pend)
+#define lex_eol_p(p) ((p)->lex.pcur >= (p)->lex.pend)
+#define lex_eol_n_p(p,n) ((p)->lex.pcur+(n) >= (p)->lex.pend)
static void token_info_setup(token_info *ptinfo, const char *ptr, const rb_code_location_t *loc);
static void token_info_push(struct parser_params*, const char *token, const rb_code_location_t *loc);
@@ -436,10 +666,6 @@ static void token_info_drop(struct parser_params *p, const char *token, rb_code_
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
-#define ANON_BLOCK_ID '&'
-#define ANON_REST_ID '*'
-#define ANON_KEYWORD_REST_ID idPow
-
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
#ifndef RIPPER
@@ -468,6 +694,11 @@ static NODE* node_newnode_with_locals(struct parser_params *, enum node_type, VA
static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE, VALUE, const rb_code_location_t*);
#define rb_node_newnode(type, a1, a2, a3, loc) node_newnode(p, (type), (a1), (a2), (a3), (loc))
+/* Make a new temporal node, which should not be appeared in the
+ * result AST and does not have node_id and location. */
+static NODE* node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2);
+#define NODE_NEW_TEMPORAL(t,a0,a1,a2) node_new_temporal(p, (t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+
static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc);
static int
@@ -665,6 +896,9 @@ VALUE rb_parser_lex_state_name(enum lex_state_e state);
void rb_parser_show_bitstack(struct parser_params *, stack_type, const char *, int);
PRINTF_ARGS(void rb_parser_fatal(struct parser_params *p, const char *fmt, ...), 2, 3);
YYLTYPE *rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_heredoc_t *here, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_delayed_token(struct parser_params *p, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_heredoc_end(struct parser_params *p, YYLTYPE *yylloc);
+YYLTYPE *rb_parser_set_location_of_dummy_end(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc);
RUBY_SYMBOL_EXPORT_END
@@ -712,12 +946,10 @@ static void numparam_pop(struct parser_params *p, NODE *prev_inner);
#endif
#define idFWD_REST '*'
-#ifdef RUBY3_KEYWORDS
#define idFWD_KWREST idPow /* Use simple "**", as tDSTAR is "**arg" */
-#else
-#define idFWD_KWREST 0
-#endif
#define idFWD_BLOCK '&'
+#define idFWD_ALL idDot3
+#define FORWARD_ARGS_WITH_RUBY2_KEYWORDS
#define RE_OPTION_ONCE (1<<16)
#define RE_OPTION_ENCODING_SHIFT 8
@@ -996,10 +1228,13 @@ rescued_expr(struct parser_params *p, NODE *arg, NODE *rescue,
static void
restore_defun(struct parser_params *p, NODE *name)
{
- YYSTYPE c = {.val = name->nd_cval};
+ NODE *save = name->nd_next;
+ YYSTYPE c = {.val = save->nd_cval};
p->cur_arg = name->nd_vid;
p->ctxt.in_def = c.ctxt.in_def;
p->ctxt.shareable_constant_value = c.ctxt.shareable_constant_value;
+ p->max_numparam = (int)save->nd_nth;
+ numparam_pop(p, save->nd_head);
}
static void
@@ -1015,6 +1250,8 @@ endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
token_info_drop(p, "def", loc->beg_pos);
}
+#define debug_token_line(p, name, line) if (p->debug) rb_parser_printf(p, name ":%d (%d: %ld|%ld|%ld)\n", line, p->ruby_sourceline, p->lex.ptok - p->lex.pbeg, p->lex.pcur - p->lex.ptok, p->lex.pend - p->lex.pcur)
+
#ifndef RIPPER
# define Qnone 0
# define Qnull 0
@@ -1101,6 +1338,14 @@ static int looking_at_eol_p(struct parser_params *p);
%define parse.error verbose
%printer {
#ifndef RIPPER
+ if ($$) {
+ rb_parser_printf(p, "%s", ruby_node_name(nd_type($$)));
+ }
+#else
+#endif
+} <node>
+%printer {
+#ifndef RIPPER
rb_parser_printf(p, "%"PRIsVALUE, rb_id2str($$));
#else
rb_parser_printf(p, "%"PRIsVALUE, RNODE($$)->nd_rval);
@@ -1213,6 +1458,7 @@ static int looking_at_eol_p(struct parser_params *p);
%token <node> tBACK_REF "back reference"
%token <node> tSTRING_CONTENT "literal content"
%token <num> tREGEXP_END
+%token <num> tDUMNY_END "dummy end"
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents regexp_contents string_content
@@ -1305,6 +1551,9 @@ static int looking_at_eol_p(struct parser_params *p);
%token tSTRING_DEND "'}'"
%token tSTRING_DBEG tSTRING_DVAR tLAMBEG tLABEL_END
+%token tIGNORED_NL tCOMMENT tEMBDOC_BEG tEMBDOC tEMBDOC_END
+%token tHEREDOC_BEG tHEREDOC_END k__END__
+
/*
* precedence table
*/
@@ -1389,10 +1638,6 @@ top_stmts : none
/*% %*/
/*% ripper: stmts_add!($1, $3) %*/
}
- | error top_stmt
- {
- $$ = remove_begin($2);
- }
;
top_stmt : stmt
@@ -1462,10 +1707,6 @@ stmts : none
/*% %*/
/*% ripper: stmts_add!($1, $3) %*/
}
- | error stmt
- {
- $$ = remove_begin($2);
- }
;
stmt_or_begin : stmt
@@ -1618,6 +1859,12 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
/*% ripper: massign!($1, $4) %*/
}
| expr
+ | error
+ {
+ /*%%%*/
+ $$ = NEW_ERROR(&@$);
+ /*% %*/
+ }
;
command_asgn : lhs '=' lex_ctxt command_rhs
@@ -1669,7 +1916,7 @@ command_asgn : lhs '=' lex_ctxt command_rhs
/*%%%*/
$$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
/*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
}
| defn_head f_opt_paren_args '=' command
{
@@ -1774,14 +2021,18 @@ expr : command_call
p->ctxt.in_kwarg = 1;
$<tbl>$ = push_pvtbl(p);
}
+ {
+ $<tbl>$ = push_pktbl(p);
+ }
p_top_expr_body
{
+ pop_pktbl(p, $<tbl>4);
pop_pvtbl(p, $<tbl>3);
p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
/*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($4, 0, 0, &@4), &@$);
+ $$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$);
/*% %*/
- /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/
+ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
}
| arg keyword_in
{
@@ -1792,34 +2043,43 @@ expr : command_call
p->ctxt.in_kwarg = 1;
$<tbl>$ = push_pvtbl(p);
}
+ {
+ $<tbl>$ = push_pktbl(p);
+ }
p_top_expr_body
{
+ pop_pktbl(p, $<tbl>4);
pop_pvtbl(p, $<tbl>3);
p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
/*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($4, NEW_TRUE(&@4), NEW_FALSE(&@4), &@4), &@$);
+ $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$);
/*% %*/
- /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/
+ /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
}
| arg %prec tLBRACE_ARG
;
-def_name : fname
- {
- ID fname = get_id($1);
- ID cur_arg = p->cur_arg;
- YYSTYPE c = {.ctxt = p->ctxt};
- numparam_name(p, fname);
- local_push(p, 0);
- p->cur_arg = 0;
- p->ctxt.in_def = 1;
- $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*cval*/c.val, &@$);
- /*%%%*/
- /*%
+def_name : fname
+ {
+ ID fname = get_id($1);
+ ID cur_arg = p->cur_arg;
+ YYSTYPE c = {.ctxt = p->ctxt};
+ numparam_name(p, fname);
+ NODE *save =
+ NODE_NEW_TEMPORAL(NODE_SELF,
+ /*head*/numparam_push(p),
+ /*nth*/p->max_numparam,
+ /*cval*/c.val);
+ local_push(p, 0);
+ p->cur_arg = 0;
+ p->ctxt.in_def = 1;
+ $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*args*/save, &@$);
+ /*%%%*/
+ /*%
$$ = NEW_RIPPER(fname, get_value($1), $$, &NULL_LOC);
%*/
- }
- ;
+ }
+ ;
defn_head : k_def def_name
{
@@ -1854,6 +2114,12 @@ expr_value : expr
value_expr($1);
$$ = $1;
}
+ | error
+ {
+ /*%%%*/
+ $$ = NEW_ERROR(&@$);
+ /*% %*/
+ }
;
expr_value_do : {COND_PUSH(1);} expr_value do {COND_POP();}
@@ -1935,14 +2201,14 @@ command : fcall command_args %prec tLOWEST
/*%%%*/
$$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, Qnull, &@3, &@$);
/*% %*/
- /*% ripper: command_call!($1, ID2VAL(idCOLON2), $3, $4) %*/
+ /*% ripper: command_call!($1, $2, $3, $4) %*/
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block
{
/*%%%*/
$$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, $5, &@3, &@$);
/*% %*/
- /*% ripper: method_add_block!(command_call!($1, ID2VAL(idCOLON2), $3, $4), $5) %*/
+ /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
}
| keyword_super command_args
{
@@ -2222,7 +2488,7 @@ lhs : user_variable
/*%%%*/
$$ = attrset(p, $1, idCOLON2, $3, &@$);
/*% %*/
- /*% ripper: field!($1, ID2VAL(idCOLON2), $3) %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value call_op tCONSTANT
{
@@ -2413,7 +2679,7 @@ arg : lhs '=' lex_ctxt arg_rhs
/*%%%*/
$$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
/*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt arg_rhs
{
@@ -2862,11 +3128,11 @@ block_arg : tAMPER arg_value
}
| tAMPER
{
- if (!local_id(p, ANON_BLOCK_ID)) {
+ if (!local_id(p, idFWD_BLOCK)) {
compile_error(p, "no anonymous block parameter");
}
/*%%%*/
- $$ = NEW_BLOCK_PASS(NEW_LVAR(ANON_BLOCK_ID, &@1), &@$);
+ $$ = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, &@1), &@$);
/*% %*/
/*% ripper: Qnil %*/
}
@@ -2899,11 +3165,12 @@ args : arg_value
}
| tSTAR
{
- if (!local_id(p, ANON_REST_ID)) {
+ if (!local_id(p, idFWD_REST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous rest parameter");
}
/*%%%*/
- $$ = NEW_SPLAT(NEW_LVAR(ANON_REST_ID, &@1), &@$);
+ $$ = NEW_SPLAT(NEW_LVAR(idFWD_REST, &@1), &@$);
/*% %*/
/*% ripper: args_add_star!(args_new!, Qnil) %*/
}
@@ -2923,11 +3190,12 @@ args : arg_value
}
| args ',' tSTAR
{
- if (!local_id(p, ANON_REST_ID)) {
+ if (!local_id(p, idFWD_REST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous rest parameter");
}
/*%%%*/
- $$ = rest_arg_append(p, $1, NEW_LVAR(ANON_REST_ID, &@3), &@$);
+ $$ = rest_arg_append(p, $1, NEW_LVAR(idFWD_REST, &@3), &@$);
/*% %*/
/*% ripper: args_add_star!($1, Qnil) %*/
}
@@ -3298,28 +3566,38 @@ primary : literal
}
| defn_head
f_arglist
+ {
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
bodystmt
k_end
{
restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $3, &@$);
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
/*% %*/
- /*% ripper: def!(get_value($1), $2, $3) %*/
+ /*% ripper: def!(get_value($1), $2, $4) %*/
local_pop(p);
}
| defs_head
f_arglist
+ {
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
bodystmt
k_end
{
restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $3, &@$);
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
/*%
$1 = get_value($1);
%*/
- /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $3) %*/
+ /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $4) %*/
local_pop(p);
}
| keyword_break
@@ -3362,6 +3640,9 @@ primary_value : primary
k_begin : keyword_begin
{
token_info_push(p, "begin", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3371,7 +3652,7 @@ k_if : keyword_if
token_info_push(p, "if", &@$);
if (p->token_info && p->token_info->nonspc &&
p->token_info->next && !strcmp(p->token_info->next->token, "else")) {
- const char *tok = p->lex.ptok;
+ const char *tok = p->lex.ptok - rb_strlen_lit("if");
const char *beg = p->lex.pbeg + p->token_info->next->beg.column;
beg += rb_strlen_lit("else");
while (beg < tok && ISSPACE(*beg)) beg++;
@@ -3379,36 +3660,54 @@ k_if : keyword_if
p->token_info->nonspc = 0;
}
}
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_unless : keyword_unless
{
token_info_push(p, "unless", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_while : keyword_while
{
token_info_push(p, "while", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_until : keyword_until
{
token_info_push(p, "until", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_case : keyword_case
{
token_info_push(p, "case", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
k_for : keyword_for
{
token_info_push(p, "for", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3416,6 +3715,9 @@ k_class : keyword_class
{
token_info_push(p, "class", &@$);
$<ctxt>$ = p->ctxt;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3423,6 +3725,9 @@ k_module : keyword_module
{
token_info_push(p, "module", &@$);
$<ctxt>$ = p->ctxt;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3436,12 +3741,19 @@ k_def : keyword_def
k_do : keyword_do
{
token_info_push(p, "do", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+
}
;
k_do_block : keyword_do_block
{
token_info_push(p, "do", &@$);
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
}
;
@@ -3488,6 +3800,13 @@ k_elsif : keyword_elsif
k_end : keyword_end
{
token_info_pop(p, "end", &@$);
+ /*%%%*/
+ pop_end_expect_token_locations(p);
+ /*% %*/
+ }
+ | tDUMNY_END
+ {
+ compile_error(p, "syntax error, unexpected end-of-input");
}
;
@@ -3853,9 +4172,15 @@ lambda_body : tLAMBEG compstmt '}'
token_info_pop(p, "}", &@3);
$$ = $2;
}
- | keyword_do_LAMBDA bodystmt k_end
+ | keyword_do_LAMBDA
{
- $$ = $2;
+ /*%%%*/
+ push_end_expect_token_locations(p, &@1.beg_pos);
+ /*% %*/
+ }
+ bodystmt k_end
+ {
+ $$ = $3;
}
;
@@ -3929,14 +4254,14 @@ method_call : fcall paren_args
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, &@3, &@$);
nd_set_line($$, @3.end_pos.lineno);
/*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), $3), $4) %*/
+ /*% ripper: method_add_arg!(call!($1, $2, $3), $4) %*/
}
| primary_value tCOLON2 operation3
{
/*%%%*/
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, Qnull, &@3, &@$);
/*% %*/
- /*% ripper: call!($1, ID2VAL(idCOLON2), $3) %*/
+ /*% ripper: call!($1, $2, $3) %*/
}
| primary_value call_op paren_args
{
@@ -3952,7 +4277,7 @@ method_call : fcall paren_args
$$ = new_qcall(p, ID2VAL(idCOLON2), $1, ID2VAL(idCall), $3, &@2, &@$);
nd_set_line($$, @2.end_pos.lineno);
/*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), ID2VAL(idCall)), $3) %*/
+ /*% ripper: method_add_arg!(call!($1, $2, ID2VAL(idCall)), $3) %*/
}
| keyword_super paren_args
{
@@ -4593,7 +4918,7 @@ p_var_ref : '^' tIDENTIFIER
}
;
-p_expr_ref : '^' tLPAREN expr_value ')'
+p_expr_ref : '^' tLPAREN expr_value rparen
{
/*%%%*/
$$ = NEW_BEGIN($3, &@$);
@@ -4633,7 +4958,16 @@ opt_rescue : k_rescue exc_list exc_var then
$$ = NEW_RESBODY($2,
$3 ? block_append(p, node_assign(p, $3, NEW_ERRINFO(&@3), NO_LEX_CTXT, &@3), $5) : $5,
$6, &@$);
- fixpos($$, $2?$2:$5);
+
+ if ($2) {
+ fixpos($$, $2);
+ }
+ else if ($3) {
+ fixpos($$, $3);
+ }
+ else {
+ fixpos($$, $5);
+ }
/*% %*/
/*% ripper: rescue!(escape_Qundef($2), escape_Qundef($3), escape_Qundef($5), escape_Qundef($6)) %*/
}
@@ -5202,6 +5536,9 @@ args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
{
add_forwarding_args(p);
$$ = new_args_tail(p, Qnone, $1, ID2VAL(idFWD_BLOCK), &@1);
+ /*%%%*/
+ ($$->nd_ainfo)->forwarding = 1;
+ /*% %*/
}
;
@@ -5281,7 +5618,11 @@ f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
args_forward : tBDOT3
{
/*%%%*/
+#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ $$ = 0;
+#else
$$ = idFWD_KWREST;
+#endif
/*% %*/
/*% ripper: args_forward! %*/
}
@@ -5490,8 +5831,9 @@ f_kwrest : kwrest_mark tIDENTIFIER
}
| kwrest_mark
{
- arg_var(p, ANON_KEYWORD_REST_ID);
+ arg_var(p, idFWD_KWREST);
/*%%%*/
+ $$ = idFWD_KWREST;
/*% %*/
/*% ripper: kwrest_param!(Qnil) %*/
}
@@ -5565,8 +5907,9 @@ f_rest_arg : restarg_mark tIDENTIFIER
}
| restarg_mark
{
- arg_var(p, ANON_REST_ID);
+ arg_var(p, idFWD_REST);
/*%%%*/
+ $$ = idFWD_REST;
/*% %*/
/*% ripper: rest_param!(Qnil) %*/
}
@@ -5586,8 +5929,9 @@ f_block_arg : blkarg_mark tIDENTIFIER
}
| blkarg_mark
{
- arg_var(p, ANON_BLOCK_ID);
+ arg_var(p, idFWD_BLOCK);
/*%%%*/
+ $$ = idFWD_BLOCK;
/*% %*/
/*% ripper: blockarg!(Qnil) %*/
}
@@ -5721,12 +6065,13 @@ assoc : arg_value tASSOC arg_value
}
| tDSTAR
{
- if (!local_id(p, ANON_KEYWORD_REST_ID)) {
+ if (!local_id(p, idFWD_KWREST) ||
+ local_id(p, idFWD_ALL)) {
compile_error(p, "no anonymous keyword rest parameter");
}
/*%%%*/
$$ = list_append(p, NEW_LIST(0, &@$),
- NEW_LVAR(ANON_KEYWORD_REST_ID, &@$));
+ NEW_LVAR(idFWD_KWREST, &@$));
/*% %*/
/*% ripper: assoc_splat!(Qnil) %*/
}
@@ -5780,7 +6125,11 @@ trailer : opt_nl
;
term : ';' {yyerrok;token_flush(p);}
- | '\n' {token_flush(p);}
+ | '\n'
+ {
+ @$.end_pos = @$.beg_pos;
+ token_flush(p);
+ }
;
terms : term
@@ -5841,12 +6190,91 @@ ripper_yylval_id(struct parser_params *p, ID x)
#endif
#define set_yylval_noname() set_yylval_id(keyword_nil)
+#define has_delayed_token(p) (!NIL_P(p->delayed.token))
#ifndef RIPPER
#define literal_flush(p, ptr) ((p)->lex.ptok = (ptr))
-#define dispatch_scan_event(p, t) ((void)0)
-#define dispatch_delayed_token(p, t) ((void)0)
-#define has_delayed_token(p) (0)
+#define dispatch_scan_event(p, t) parser_dispatch_scan_event(p, t, __LINE__)
+
+static bool
+parser_has_token(struct parser_params *p)
+{
+ if (p->keep_tokens && (p->lex.pcur < p->lex.ptok)) rb_bug("lex.pcur < lex.ptok. (line: %d) %ld|%ld|%ld", p->ruby_sourceline, p->lex.ptok - p->lex.pbeg, p->lex.pcur - p->lex.ptok, p->lex.pend - p->lex.pcur);
+ return p->lex.pcur > p->lex.ptok;
+}
+
+static VALUE
+code_loc_to_ary(const rb_code_location_t *loc)
+{
+ VALUE ary = rb_ary_new_from_args(4,
+ INT2NUM(loc->beg_pos.lineno), INT2NUM(loc->beg_pos.column),
+ INT2NUM(loc->end_pos.lineno), INT2NUM(loc->end_pos.column));
+ rb_obj_freeze(ary);
+
+ return ary;
+}
+
+static void
+parser_append_tokens(struct parser_params *p, VALUE str, enum yytokentype t, int line)
+{
+ VALUE ary;
+ int token_id;
+
+ ary = rb_ary_new2(4);
+ token_id = p->token_id;
+ rb_ary_push(ary, INT2FIX(token_id));
+ rb_ary_push(ary, ID2SYM(parser_token2id(t)));
+ rb_ary_push(ary, str);
+ rb_ary_push(ary, code_loc_to_ary(p->yylloc));
+ rb_obj_freeze(ary);
+ rb_ary_push(p->tokens, ary);
+ p->token_id++;
+
+ if (p->debug) {
+ rb_parser_printf(p, "Append tokens (line: %d) %"PRIsVALUE"\n", line, ary);
+ }
+}
+
+static void
+parser_dispatch_scan_event(struct parser_params *p, enum yytokentype t, int line)
+{
+ debug_token_line(p, "parser_dispatch_scan_event", line);
+
+ if (!parser_has_token(p)) return;
+
+ RUBY_SET_YYLLOC(*p->yylloc);
+
+ if (p->keep_tokens) {
+ VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
+ parser_append_tokens(p, str, t, line);
+ }
+
+ token_flush(p);
+}
+
+#define dispatch_delayed_token(p, t) parser_dispatch_delayed_token(p, t, __LINE__)
+static void
+parser_dispatch_delayed_token(struct parser_params *p, enum yytokentype t, int line)
+{
+ int saved_line = p->ruby_sourceline;
+ const char *saved_tokp = p->lex.ptok;
+
+ debug_token_line(p, "parser_dispatch_delayed_token", line);
+
+ if (!has_delayed_token(p)) return;
+
+ RUBY_SET_YYLLOC_OF_DELAYED_TOKEN(*p->yylloc);
+
+ if (p->keep_tokens) {
+ p->ruby_sourceline = p->delayed.beg_line;
+ p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
+ parser_append_tokens(p, p->delayed.token, t, line);
+ p->ruby_sourceline = saved_line;
+ p->lex.ptok = saved_tokp;
+ }
+
+ p->delayed.token = Qnil;
+}
#else
#define literal_flush(p, ptr) ((void)(ptr))
@@ -5871,6 +6299,7 @@ ripper_scan_event_val(struct parser_params *p, enum yytokentype t)
{
VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
VALUE rval = ripper_dispatch1(p, ripper_token2eventid(t), str);
+ RUBY_SET_YYLLOC(*p->yylloc);
token_flush(p);
return rval;
}
@@ -5889,16 +6318,15 @@ ripper_dispatch_delayed_token(struct parser_params *p, enum yytokentype t)
int saved_line = p->ruby_sourceline;
const char *saved_tokp = p->lex.ptok;
- if (NIL_P(p->delayed.token)) return;
- p->ruby_sourceline = p->delayed.line;
- p->lex.ptok = p->lex.pbeg + p->delayed.col;
+ if (!has_delayed_token(p)) return;
+ p->ruby_sourceline = p->delayed.beg_line;
+ p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
add_mark_object(p, yylval_rval = ripper_dispatch1(p, ripper_token2eventid(t), p->delayed.token));
p->delayed.token = Qnil;
p->ruby_sourceline = saved_line;
p->lex.ptok = saved_tokp;
}
#define dispatch_delayed_token(p, t) ripper_dispatch_delayed_token(p, t)
-#define has_delayed_token(p) (!NIL_P(p->delayed.token))
#endif /* RIPPER */
static inline int
@@ -6369,14 +6797,15 @@ yycompile0(VALUE arg)
p->lex.strterm = 0;
p->lex.pcur = p->lex.pbeg = p->lex.pend = 0;
- p->lex.prevline = p->lex.lastline = p->lex.nextline = 0;
if (n || p->error_p) {
VALUE mesg = p->error_buffer;
if (!mesg) {
mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
}
- rb_set_errinfo(mesg);
- return FALSE;
+ if (!p->error_tolerant) {
+ rb_set_errinfo(mesg);
+ return FALSE;
+ }
}
tree = p->eval_tree;
if (!tree) {
@@ -6384,6 +6813,7 @@ yycompile0(VALUE arg)
}
else {
VALUE opt = p->compile_option;
+ VALUE tokens = p->tokens;
NODE *prelude;
NODE *body = parser_append_options(p, tree->nd_body);
if (!opt) opt = rb_obj_hide(rb_ident_hash_new());
@@ -6391,6 +6821,10 @@ yycompile0(VALUE arg)
prelude = block_append(p, p->eval_tree_begin, body);
tree->nd_body = prelude;
RB_OBJ_WRITE(p->ast, &p->ast->body.compile_option, opt);
+ if (p->keep_tokens) {
+ rb_obj_freeze(tokens);
+ rb_ast_set_tokens(p->ast, tokens);
+ }
}
p->ast->body.root = tree;
if (!p->ast->body.script_lines) p->ast->body.script_lines = INT2FIX(p->line_count);
@@ -6573,7 +7007,7 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
if (!(func & STR_FUNC_REGEXP) && rb_enc_asciicompat(enc)) {
if (is_ascii_string(str)) {
}
- else if (enc0 == rb_usascii_encoding() && enc != rb_utf8_encoding()) {
+ else if (rb_is_usascii_enc(enc0) && enc != rb_utf8_encoding()) {
rb_enc_associate(str, rb_ascii8bit_encoding());
}
}
@@ -6581,32 +7015,31 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
return str;
}
-#define lex_goto_eol(p) ((p)->lex.pcur = (p)->lex.pend)
-#define lex_eol_p(p) ((p)->lex.pcur >= (p)->lex.pend)
-#define lex_eol_n_p(p,n) ((p)->lex.pcur+(n) >= (p)->lex.pend)
#define peek(p,c) peek_n(p, (c), 0)
#define peek_n(p,c,n) (!lex_eol_n_p(p, n) && (c) == (unsigned char)(p)->lex.pcur[n])
#define peekc(p) peekc_n(p, 0)
#define peekc_n(p,n) (lex_eol_n_p(p, n) ? -1 : (unsigned char)(p)->lex.pcur[n])
-#ifdef RIPPER
static void
-add_delayed_token(struct parser_params *p, const char *tok, const char *end)
+add_delayed_token(struct parser_params *p, const char *tok, const char *end, int line)
{
+#ifndef RIPPER
+ debug_token_line(p, "add_delayed_token", line);
+#endif
+
if (tok < end) {
if (!has_delayed_token(p)) {
p->delayed.token = rb_str_buf_new(end - tok);
rb_enc_associate(p->delayed.token, p->enc);
- p->delayed.line = p->ruby_sourceline;
- p->delayed.col = rb_long2int(tok - p->lex.pbeg);
+ p->delayed.beg_line = p->ruby_sourceline;
+ p->delayed.beg_col = rb_long2int(tok - p->lex.pbeg);
}
rb_str_buf_cat(p->delayed.token, tok, end - tok);
+ p->delayed.end_line = p->ruby_sourceline;
+ p->delayed.end_col = rb_long2int(end - p->lex.pbeg);
p->lex.ptok = end;
}
}
-#else
-#define add_delayed_token(p, tok, end) ((void)(tok), (void)(end))
-#endif
static int
nextline(struct parser_params *p, int set_encoding)
@@ -6639,7 +7072,7 @@ nextline(struct parser_params *p, int set_encoding)
/* after here-document without terminator */
goto end_of_input;
}
- add_delayed_token(p, p->lex.ptok, p->lex.pend);
+ add_delayed_token(p, p->lex.ptok, p->lex.pend, __LINE__);
if (p->heredoc_end > 0) {
p->ruby_sourceline = p->heredoc_end;
p->heredoc_end = 0;
@@ -6648,7 +7081,6 @@ nextline(struct parser_params *p, int set_encoding)
p->lex.pbeg = p->lex.pcur = RSTRING_PTR(v);
p->lex.pend = p->lex.pcur + RSTRING_LEN(v);
token_flush(p);
- p->lex.prevline = p->lex.lastline;
p->lex.lastline = v;
return 0;
}
@@ -6801,20 +7233,22 @@ tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
{
size_t numlen;
int codepoint = scan_hex(p->lex.pcur, wide ? p->lex.pend - p->lex.pcur : 4, &numlen);
- literal_flush(p, p->lex.pcur);
p->lex.pcur += numlen;
if (p->lex.strterm == NULL ||
(p->lex.strterm->flags & STRTERM_HEREDOC) ||
(p->lex.strterm->u.literal.u1.func != str_regexp)) {
if (wide ? (numlen == 0 || numlen > 6) : (numlen < 4)) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode escape");
return wide && numlen > 0;
}
if (codepoint > 0x10ffff) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode codepoint (too large)");
return wide;
}
if ((codepoint & 0xfffff800) == 0xd800) {
+ literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode codepoint");
return wide;
}
@@ -6839,6 +7273,18 @@ tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
return TRUE;
}
+static int tokadd_mbchar(struct parser_params *p, int c);
+
+static int
+tokskip_mbchar(struct parser_params *p)
+{
+ int len = parser_precise_mbclen(p, p->lex.pcur-1);
+ if (len > 0) {
+ p->lex.pcur += len - 1;
+ }
+ return len;
+}
+
/* return value is for ?\u3042 */
static void
tokadd_utf8(struct parser_params *p, rb_encoding **encp,
@@ -6856,44 +7302,71 @@ tokadd_utf8(struct parser_params *p, rb_encoding **encp,
if (regexp_literal) { tokadd(p, '\\'); tokadd(p, 'u'); }
if (peek(p, open_brace)) { /* handle \u{...} form */
- const char *second = NULL;
- int c, last = nextc(p);
- if (p->lex.pcur >= p->lex.pend) goto unterminated;
- while (ISSPACE(c = *p->lex.pcur) && ++p->lex.pcur < p->lex.pend);
- while (c != close_brace) {
- if (c == term) goto unterminated;
- if (second == multiple_codepoints)
- second = p->lex.pcur;
- if (regexp_literal) tokadd(p, last);
- if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
- break;
- }
- while (ISSPACE(c = *p->lex.pcur)) {
- if (++p->lex.pcur >= p->lex.pend) goto unterminated;
- last = c;
- }
- if (term == -1 && !second)
- second = multiple_codepoints;
- }
+ if (regexp_literal && p->lex.strterm->u.literal.u1.func == str_regexp) {
+ /*
+ * Skip parsing validation code and copy bytes as-is until term or
+ * closing brace, in order to correctly handle extended regexps where
+ * invalid unicode escapes are allowed in comments. The regexp parser
+ * does its own validation and will catch any issues.
+ */
+ tokadd(p, open_brace);
+ while (++p->lex.pcur < p->lex.pend) {
+ int c = peekc(p);
+ if (c == close_brace) {
+ tokadd(p, c);
+ ++p->lex.pcur;
+ break;
+ }
+ else if (c == term) {
+ break;
+ }
+ if (c == '\\' && p->lex.pcur + 1 < p->lex.pend) {
+ tokadd(p, c);
+ c = *++p->lex.pcur;
+ }
+ tokadd_mbchar(p, c);
+ }
+ }
+ else {
+ const char *second = NULL;
+ int c, last = nextc(p);
+ if (p->lex.pcur >= p->lex.pend) goto unterminated;
+ while (ISSPACE(c = *p->lex.pcur) && ++p->lex.pcur < p->lex.pend);
+ while (c != close_brace) {
+ if (c == term) goto unterminated;
+ if (second == multiple_codepoints)
+ second = p->lex.pcur;
+ if (regexp_literal) tokadd(p, last);
+ if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
+ break;
+ }
+ while (ISSPACE(c = *p->lex.pcur)) {
+ if (++p->lex.pcur >= p->lex.pend) goto unterminated;
+ last = c;
+ }
+ if (term == -1 && !second)
+ second = multiple_codepoints;
+ }
- if (c != close_brace) {
- unterminated:
- token_flush(p);
- yyerror0("unterminated Unicode escape");
- return;
- }
- if (second && second != multiple_codepoints) {
- const char *pcur = p->lex.pcur;
- p->lex.pcur = second;
- dispatch_scan_event(p, tSTRING_CONTENT);
- token_flush(p);
- p->lex.pcur = pcur;
- yyerror0(multiple_codepoints);
- token_flush(p);
- }
+ if (c != close_brace) {
+ unterminated:
+ token_flush(p);
+ yyerror0("unterminated Unicode escape");
+ return;
+ }
+ if (second && second != multiple_codepoints) {
+ const char *pcur = p->lex.pcur;
+ p->lex.pcur = second;
+ dispatch_scan_event(p, tSTRING_CONTENT);
+ token_flush(p);
+ p->lex.pcur = pcur;
+ yyerror0(multiple_codepoints);
+ token_flush(p);
+ }
- if (regexp_literal) tokadd(p, close_brace);
- nextc(p);
+ if (regexp_literal) tokadd(p, close_brace);
+ nextc(p);
+ }
}
else { /* handle \uxxxx form */
if (!tokadd_codepoint(p, encp, regexp_literal, FALSE)) {
@@ -6968,7 +7441,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
}
return read_escape(p, flags|ESCAPE_META, encp) | 0x80;
}
- else if (c == -1 || !ISASCII(c)) goto eof;
+ else if (c == -1) goto eof;
+ else if (!ISASCII(c)) {
+ tokskip_mbchar(p);
+ goto eof;
+ }
else {
int c2 = escaped_control_code(c);
if (c2) {
@@ -6999,7 +7476,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
}
else if (c == '?')
return 0177;
- else if (c == -1 || !ISASCII(c)) goto eof;
+ else if (c == -1) goto eof;
+ else if (!ISASCII(c)) {
+ tokskip_mbchar(p);
+ goto eof;
+ }
else {
int c2 = escaped_control_code(c);
if (c2) {
@@ -7027,7 +7508,7 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
eof:
case -1:
yyerror0("Invalid escape character syntax");
- token_flush(p);
+ dispatch_scan_event(p, tSTRING_CONTENT);
return '\0';
default:
@@ -7196,6 +7677,10 @@ tokadd_string(struct parser_params *p,
{
int c;
bool erred = false;
+#ifdef RIPPER
+ const int heredoc_end = (p->heredoc_end ? p->heredoc_end + 1 : 0);
+ int top_of_line = FALSE;
+#endif
#define mixed_error(enc1, enc2) \
(void)(erred || (parser_mixed_error(p, enc1, enc2), erred = true))
@@ -7206,6 +7691,12 @@ tokadd_string(struct parser_params *p,
if (p->heredoc_indent > 0) {
parser_update_heredoc_indent(p, c);
}
+#ifdef RIPPER
+ if (top_of_line && heredoc_end == p->ruby_sourceline) {
+ pushback(p, c);
+ break;
+ }
+#endif
if (paren && c == paren) {
++*nest;
@@ -7218,14 +7709,13 @@ tokadd_string(struct parser_params *p,
--*nest;
}
else if ((func & STR_FUNC_EXPAND) && c == '#' && p->lex.pcur < p->lex.pend) {
- int c2 = *p->lex.pcur;
+ unsigned char c2 = *p->lex.pcur;
if (c2 == '$' || c2 == '@' || c2 == '{') {
pushback(p, c);
break;
}
}
else if (c == '\\') {
- literal_flush(p, p->lex.pcur - 1);
c = nextc(p);
switch (c) {
case '\n':
@@ -7332,6 +7822,9 @@ tokadd_string(struct parser_params *p,
}
}
tokadd(p, c);
+#ifdef RIPPER
+ top_of_line = (c == '\n');
+#endif
}
terminate:
if (*enc) *encp = *enc;
@@ -7370,7 +7863,21 @@ flush_string_content(struct parser_params *p, rb_encoding *enc)
yylval.val = content;
}
#else
-#define flush_string_content(p, enc) ((void)(enc))
+static void
+flush_string_content(struct parser_params *p, rb_encoding *enc)
+{
+ if (has_delayed_token(p)) {
+ ptrdiff_t len = p->lex.pcur - p->lex.ptok;
+ if (len > 0) {
+ rb_enc_str_buf_cat(p->delayed.token, p->lex.ptok, len, enc);
+ p->delayed.end_line = p->ruby_sourceline;
+ p->delayed.end_col = rb_long2int(p->lex.pcur - p->lex.pbeg);
+ }
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+ p->lex.ptok = p->lex.pcur;
+ }
+ dispatch_scan_event(p, tSTRING_CONTENT);
+}
#endif
RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
@@ -7489,14 +7996,14 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
if (func & STR_FUNC_QWORDS) {
quote->u1.func |= STR_FUNC_TERM;
pushback(p, c); /* dispatch the term at tSTRING_END */
- add_delayed_token(p, p->lex.ptok, p->lex.pcur);
+ add_delayed_token(p, p->lex.ptok, p->lex.pcur, __LINE__);
return ' ';
}
return parser_string_term(p, func);
}
if (space) {
pushback(p, c);
- add_delayed_token(p, p->lex.ptok, p->lex.pcur);
+ add_delayed_token(p, p->lex.ptok, p->lex.pcur, __LINE__);
return ' ';
}
newtok(p);
@@ -7856,12 +8363,29 @@ dispatch_heredoc_end(struct parser_params *p)
dispatch_delayed_token(p, tSTRING_CONTENT);
str = STR_NEW(p->lex.ptok, p->lex.pend - p->lex.ptok);
ripper_dispatch1(p, ripper_token2eventid(tHEREDOC_END), str);
+ RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*p->yylloc);
lex_goto_eol(p);
token_flush(p);
}
#else
-#define dispatch_heredoc_end(p) ((void)0)
+#define dispatch_heredoc_end(p) parser_dispatch_heredoc_end(p, __LINE__)
+static void
+parser_dispatch_heredoc_end(struct parser_params *p, int line)
+{
+ if (has_delayed_token(p))
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+
+ if (p->keep_tokens) {
+ VALUE str = STR_NEW(p->lex.ptok, p->lex.pend - p->lex.ptok);
+ RUBY_SET_YYLLOC_OF_HEREDOC_END(*p->yylloc);
+ parser_append_tokens(p, str, tHEREDOC_END, line);
+ }
+
+ RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*p->yylloc);
+ lex_goto_eol(p);
+ token_flush(p);
+}
#endif
static enum yytokentype
@@ -7891,7 +8415,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
int cr = ENC_CODERANGE_UNKNOWN;
rb_str_coderange_scan_restartable(p->lex.ptok, p->lex.pcur, enc, &cr);
if (cr != ENC_CODERANGE_7BIT &&
- p->enc == rb_usascii_encoding() &&
+ rb_is_usascii_enc(p->enc) &&
enc != rb_utf8_encoding()) {
enc = rb_ascii8bit_encoding();
}
@@ -8820,7 +9344,7 @@ parse_qmark(struct parser_params *p, int space_seen)
enc = rb_utf8_encoding();
tokadd_utf8(p, &enc, -1, 0, 0);
}
- else if (!lex_eol_p(p) && !(c = *p->lex.pcur, ISASCII(c))) {
+ else if (!ISASCII(c = peekc(p))) {
nextc(p);
if (tokadd_mbchar(p, c) == -1) return 0;
}
@@ -9171,6 +9695,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
int mb = ENC_CODERANGE_7BIT;
const enum lex_state_e last_state = p->lex.state;
ID ident;
+ int enforce_keyword_end = 0;
do {
if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN;
@@ -9200,7 +9725,34 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
return tLABEL;
}
}
- if (mb == ENC_CODERANGE_7BIT && !IS_lex_state(EXPR_DOT)) {
+
+#ifndef RIPPER
+ if (!NIL_P(peek_end_expect_token_locations(p))) {
+ VALUE end_loc;
+ int lineno, column;
+ int beg_pos = (int)(p->lex.ptok - p->lex.pbeg);
+
+ end_loc = peek_end_expect_token_locations(p);
+ lineno = NUM2INT(rb_ary_entry(end_loc, 0));
+ column = NUM2INT(rb_ary_entry(end_loc, 1));
+
+ if (p->debug) {
+ rb_parser_printf(p, "enforce_keyword_end check. current: (%d, %d), peek: (%d, %d)\n",
+ p->ruby_sourceline, beg_pos, lineno, column);
+ }
+
+ if ((p->ruby_sourceline > lineno) && (beg_pos <= column)) {
+ const struct kwtable *kw;
+
+ if ((IS_lex_state(EXPR_DOT)) && (kw = rb_reserved_word(tok(p), toklen(p))) && (kw && kw->id[0] == keyword_end)) {
+ if (p->debug) rb_parser_printf(p, "enforce_keyword_end is enabled\n");
+ enforce_keyword_end = 1;
+ }
+ }
+ }
+#endif
+
+ if (mb == ENC_CODERANGE_7BIT && (!IS_lex_state(EXPR_DOT) || enforce_keyword_end)) {
const struct kwtable *kw;
/* See if it is a reserved word. */
@@ -9254,13 +9806,23 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
ident = tokenize_ident(p, last_state);
if (result == tCONSTANT && is_local_id(ident)) result = tIDENTIFIER;
if (!IS_lex_state_for(last_state, EXPR_DOT|EXPR_FNAME) &&
- (result == tIDENTIFIER) && /* not EXPR_FNAME, not attrasgn */
- lvar_defined(p, ident)) {
- SET_LEX_STATE(EXPR_END|EXPR_LABEL);
+ (result == tIDENTIFIER) && /* not EXPR_FNAME, not attrasgn */
+ (lvar_defined(p, ident) || NUMPARAM_ID_P(ident))) {
+ SET_LEX_STATE(EXPR_END|EXPR_LABEL);
}
return result;
}
+static void
+warn_cr(struct parser_params *p)
+{
+ if (!p->cr_seen) {
+ p->cr_seen = TRUE;
+ /* carried over with p->lex.nextline for nextc() */
+ rb_warn0("encountered \\r in middle of line, treated as a mere space");
+ }
+}
+
static enum yytokentype
parser_yylex(struct parser_params *p)
{
@@ -9274,6 +9836,7 @@ parser_yylex(struct parser_params *p)
if (p->lex.strterm) {
if (p->lex.strterm->flags & STRTERM_HEREDOC) {
+ token_flush(p);
return here_document(p, &p->lex.strterm->u.heredoc);
}
else {
@@ -9284,34 +9847,41 @@ parser_yylex(struct parser_params *p)
cmd_state = p->command_start;
p->command_start = FALSE;
p->token_seen = TRUE;
- retry:
- last_state = p->lex.state;
#ifndef RIPPER
token_flush(p);
#endif
+ retry:
+ last_state = p->lex.state;
switch (c = nextc(p)) {
case '\0': /* NUL */
case '\004': /* ^D */
case '\032': /* ^Z */
case -1: /* end of script. */
p->eofp = 1;
+#ifndef RIPPER
+ if (!NIL_P(p->end_expect_token_locations) && RARRAY_LEN(p->end_expect_token_locations) > 0) {
+ pop_end_expect_token_locations(p);
+ RUBY_SET_YYLLOC_OF_DUMMY_END(*p->yylloc);
+ return tDUMNY_END;
+ }
+#endif
+ /* Set location for end-of-input because dispatch_scan_event is not called. */
+ RUBY_SET_YYLLOC(*p->yylloc);
return 0;
/* white spaces */
case '\r':
- if (!p->cr_seen) {
- p->cr_seen = TRUE;
- /* carried over with p->lex.nextline for nextc() */
- rb_warn0("encountered \\r in middle of line, treated as a mere space");
- }
+ warn_cr(p);
/* fall through */
case ' ': case '\t': case '\f':
case '\13': /* '\v' */
space_seen = 1;
-#ifdef RIPPER
while ((c = nextc(p))) {
switch (c) {
- case ' ': case '\t': case '\f': case '\r':
+ case '\r':
+ warn_cr(p);
+ /* fall through */
+ case ' ': case '\t': case '\f':
case '\13': /* '\v' */
break;
default:
@@ -9321,6 +9891,8 @@ parser_yylex(struct parser_params *p)
outofloop:
pushback(p, c);
dispatch_scan_event(p, tSP);
+#ifndef RIPPER
+ token_flush(p);
#endif
goto retry;
@@ -9358,7 +9930,10 @@ parser_yylex(struct parser_params *p)
break;
case '#':
pushback(p, c);
- if (space_seen) dispatch_scan_event(p, tSP);
+ if (space_seen) {
+ dispatch_scan_event(p, tSP);
+ token_flush(p);
+ }
goto retry;
case '&':
case '.': {
@@ -9373,18 +9948,10 @@ parser_yylex(struct parser_params *p)
p->ruby_sourceline--;
p->lex.nextline = p->lex.lastline;
case -1: /* EOF no decrement*/
-#ifndef RIPPER
- if (p->lex.prevline && !p->eofp) p->lex.lastline = p->lex.prevline;
- p->lex.pbeg = RSTRING_PTR(p->lex.lastline);
- p->lex.pend = p->lex.pcur = p->lex.pbeg + RSTRING_LEN(p->lex.lastline);
- pushback(p, 1); /* always pushback */
- p->lex.ptok = p->lex.pcur;
-#else
lex_goto_eol(p);
if (c != -1) {
p->lex.ptok = p->lex.pcur;
}
-#endif
goto normal_newline;
}
}
@@ -9982,12 +10549,9 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
p->lval = lval;
lval->val = Qundef;
- t = parser_yylex(p);
+ p->yylloc = yylloc;
- if (p->lex.strterm && (p->lex.strterm->flags & STRTERM_HEREDOC))
- RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*yylloc);
- else
- RUBY_SET_YYLLOC(*yylloc);
+ t = parser_yylex(p);
if (has_delayed_token(p))
dispatch_delayed_token(p, t);
@@ -10000,11 +10564,18 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
#define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1))
static NODE*
-node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
+node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
{
NODE *n = rb_ast_newnode(p->ast, type);
rb_node_init(n, type, a0, a1, a2);
+ return n;
+}
+
+static NODE*
+node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
+{
+ NODE *n = node_new_temporal(p, type, a0, a1, a2);
nd_set_loc(n, loc);
nd_set_node_id(n, parser_get_node_id(p));
@@ -10540,13 +11111,7 @@ static NODE *
kwd_append(NODE *kwlist, NODE *kw)
{
if (kwlist) {
- NODE *kws = kwlist;
- kws->nd_loc.end_pos = kw->nd_loc.end_pos;
- while (kws->nd_next) {
- kws = kws->nd_next;
- kws->nd_loc.end_pos = kw->nd_loc.end_pos;
- }
- kws->nd_next = kw;
+ opt_arg_append(kwlist, kw);
}
return kwlist;
}
@@ -10682,7 +11247,7 @@ check_literal_when(struct parser_params *p, NODE *arg, const YYLTYPE *loc)
if (!arg || !p->case_labels) return;
lit = rb_node_case_when_optimizable_literal(arg);
- if (lit == Qundef) return;
+ if (UNDEF_P(lit)) return;
if (nd_type_p(arg, NODE_STR)) {
RB_OBJ_WRITTEN(p->ast, Qnil, arg->nd_lit = lit);
}
@@ -10885,6 +11450,34 @@ rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_
}
YYLTYPE *
+rb_parser_set_location_of_delayed_token(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->beg_pos.lineno = p->delayed.beg_line;
+ yylloc->beg_pos.column = p->delayed.beg_col;
+ yylloc->end_pos.lineno = p->delayed.end_line;
+ yylloc->end_pos.column = p->delayed.end_col;
+
+ return yylloc;
+}
+
+YYLTYPE *
+rb_parser_set_location_of_heredoc_end(struct parser_params *p, YYLTYPE *yylloc)
+{
+ int sourceline = p->ruby_sourceline;
+ int beg_pos = (int)(p->lex.ptok - p->lex.pbeg);
+ int end_pos = (int)(p->lex.pend - p->lex.pbeg);
+ return rb_parser_set_pos(yylloc, sourceline, beg_pos, end_pos);
+}
+
+YYLTYPE *
+rb_parser_set_location_of_dummy_end(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->end_pos = yylloc->beg_pos;
+
+ return yylloc;
+}
+
+YYLTYPE *
rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc)
{
int sourceline = p->ruby_sourceline;
@@ -11335,7 +11928,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
}
if (RTEST(lit)) {
VALUE e = shareable_literal_value(elt);
- if (e != Qundef) {
+ if (!UNDEF_P(e)) {
rb_ary_push(lit, e);
}
else {
@@ -11375,7 +11968,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
if (RTEST(lit)) {
VALUE k = shareable_literal_value(key);
VALUE v = shareable_literal_value(val);
- if (k != Qundef && v != Qundef) {
+ if (!UNDEF_P(k) && !UNDEF_P(v)) {
rb_hash_aset(lit, k, v);
}
else {
@@ -12038,7 +12631,7 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N
int saved_line = p->ruby_sourceline;
struct rb_args_info *args = tail->nd_ainfo;
- if (args->block_arg == idFWD_BLOCK) {
+ if (args->forwarding) {
if (rest_arg) {
yyerror1(&tail->nd_loc, "... after rest argument");
return tail;
@@ -12057,7 +12650,11 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N
args->opt_args = opt_args;
- args->ruby2_keywords = rest_arg == idFWD_REST;
+#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ args->ruby2_keywords = args->forwarding;
+#else
+ args->ruby2_keywords = 0;
+#endif
p->ruby_sourceline = saved_line;
nd_set_loc(tail, loc);
@@ -12617,26 +13214,43 @@ local_push(struct parser_params *p, int toplevel_scope)
}
static void
+vtable_chain_free(struct parser_params *p, struct vtable *table)
+{
+ while (!DVARS_TERMINAL_P(table)) {
+ struct vtable *cur_table = table;
+ table = cur_table->prev;
+ vtable_free(cur_table);
+ }
+}
+
+static void
+local_free(struct parser_params *p, struct local_vars *local)
+{
+ vtable_chain_free(p, local->used);
+
+# if WARN_PAST_SCOPE
+ vtable_chain_free(p, local->past);
+# endif
+
+ vtable_chain_free(p, local->args);
+ vtable_chain_free(p, local->vars);
+
+ ruby_sized_xfree(local, sizeof(struct local_vars));
+}
+
+static void
local_pop(struct parser_params *p)
{
struct local_vars *local = p->lvtbl->prev;
if (p->lvtbl->used) {
- warn_unused_var(p, p->lvtbl);
- vtable_free(p->lvtbl->used);
+ warn_unused_var(p, p->lvtbl);
}
-# if WARN_PAST_SCOPE
- while (p->lvtbl->past) {
- struct vtable *past = p->lvtbl->past;
- p->lvtbl->past = past->prev;
- vtable_free(past);
- }
-# endif
- vtable_free(p->lvtbl->args);
- vtable_free(p->lvtbl->vars);
+
+ local_free(p, p->lvtbl);
+ p->lvtbl = local;
+
CMDARG_POP();
COND_POP();
- ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
- p->lvtbl = local;
}
#ifndef RIPPER
@@ -12741,11 +13355,7 @@ local_id(struct parser_params *p, ID id)
static int
check_forwarding_args(struct parser_params *p)
{
- if (local_id(p, idFWD_REST) &&
-#if idFWD_KWREST
- local_id(p, idFWD_KWREST) &&
-#endif
- local_id(p, idFWD_BLOCK)) return TRUE;
+ if (local_id(p, idFWD_ALL)) return TRUE;
compile_error(p, "unexpected ...");
return FALSE;
}
@@ -12754,24 +13364,25 @@ static void
add_forwarding_args(struct parser_params *p)
{
arg_var(p, idFWD_REST);
-#if idFWD_KWREST
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
arg_var(p, idFWD_KWREST);
#endif
arg_var(p, idFWD_BLOCK);
+ arg_var(p, idFWD_ALL);
}
#ifndef RIPPER
static NODE *
new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc)
{
- NODE *splat = NEW_SPLAT(NEW_LVAR(idFWD_REST, loc), loc);
-#if idFWD_KWREST
+ NODE *rest = NEW_LVAR(idFWD_REST, loc);
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc));
#endif
NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), loc);
- NODE *args = leading ? rest_arg_append(p, leading, splat, argsloc) : splat;
-#if idFWD_KWREST
- args = arg_append(p, splat, new_hash(p, kwrest, loc), loc);
+ NODE *args = leading ? rest_arg_append(p, leading, rest, argsloc) : NEW_SPLAT(rest, loc);
+#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+ args = arg_append(p, args, new_hash(p, kwrest, loc), loc);
#endif
return arg_blk_pass(args, block);
}
@@ -12954,7 +13565,7 @@ rb_reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
}
rb_enc_associate(str, rb_ascii8bit_encoding());
}
- else if (p->enc == rb_usascii_encoding()) {
+ else if (rb_is_usascii_enc(p->enc)) {
if (!is_ascii_string(str)) {
/* raise in re.c */
rb_enc_associate(str, rb_usascii_encoding());
@@ -13160,12 +13771,15 @@ parser_initialize(struct parser_params *p)
p->ruby_sourcefile_string = Qnil;
p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
p->node_id = 0;
-#ifdef RIPPER
p->delayed.token = Qnil;
+#ifdef RIPPER
p->result = Qnil;
p->parsing_thread = Qnil;
#else
p->error_buffer = Qfalse;
+ p->end_expect_token_locations = Qnil;
+ p->token_id = 0;
+ p->tokens = Qnil;
#endif
p->debug_buffer = Qnil;
p->debug_output = rb_ractor_stdout();
@@ -13183,19 +13797,20 @@ parser_mark(void *ptr)
struct parser_params *p = (struct parser_params*)ptr;
rb_gc_mark(p->lex.input);
- rb_gc_mark(p->lex.prevline);
rb_gc_mark(p->lex.lastline);
rb_gc_mark(p->lex.nextline);
rb_gc_mark(p->ruby_sourcefile_string);
rb_gc_mark((VALUE)p->lex.strterm);
rb_gc_mark((VALUE)p->ast);
rb_gc_mark(p->case_labels);
+ rb_gc_mark(p->delayed.token);
#ifndef RIPPER
rb_gc_mark(p->debug_lines);
rb_gc_mark(p->compile_option);
rb_gc_mark(p->error_buffer);
+ rb_gc_mark(p->end_expect_token_locations);
+ rb_gc_mark(p->tokens);
#else
- rb_gc_mark(p->delayed.token);
rb_gc_mark(p->value);
rb_gc_mark(p->result);
rb_gc_mark(p->parsing_thread);
@@ -13216,16 +13831,17 @@ parser_free(void *ptr)
if (p->tokenbuf) {
ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
+
for (local = p->lvtbl; local; local = prev) {
- if (local->vars) xfree(local->vars);
- prev = local->prev;
- xfree(local);
+ prev = local->prev;
+ local_free(p, local);
}
+
{
- token_info *ptinfo;
- while ((ptinfo = p->token_info) != 0) {
- p->token_info = ptinfo->next;
- xfree(ptinfo);
+ token_info *ptinfo;
+ while ((ptinfo = p->token_info) != 0) {
+ p->token_info = ptinfo->next;
+ xfree(ptinfo);
}
}
xfree(ptr);
@@ -13298,6 +13914,27 @@ rb_parser_keep_script_lines(VALUE vparser)
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
p->keep_script_lines = 1;
}
+
+void
+rb_parser_error_tolerant(VALUE vparser)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->error_tolerant = 1;
+ p->end_expect_token_locations = rb_ary_new();
+}
+
+void
+rb_parser_keep_tokens(VALUE vparser)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->keep_tokens = 1;
+ p->tokens = rb_ary_new();
+}
+
#endif
#ifdef RIPPER
@@ -13608,7 +14245,7 @@ ripper_validate_object(VALUE self, VALUE x)
if (x == Qfalse) return x;
if (x == Qtrue) return x;
if (NIL_P(x)) return x;
- if (x == Qundef)
+ if (UNDEF_P(x))
rb_raise(rb_eArgError, "Qundef given");
if (FIXNUM_P(x)) return x;
if (SYMBOL_P(x)) return x;
@@ -13719,7 +14356,7 @@ static VALUE
ripper_get_value(VALUE v)
{
NODE *nd;
- if (v == Qundef) return Qnil;
+ if (UNDEF_P(v)) return Qnil;
if (!RB_TYPE_P(v, T_NODE)) return v;
nd = (NODE *)v;
if (!nd_type_p(nd, NODE_RIPPER)) return Qnil;
@@ -13980,7 +14617,7 @@ static VALUE
ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg)
{
StringValue(msg);
- if (obj == Qundef) {
+ if (UNDEF_P(obj)) {
rb_raise(rb_eArgError, "%"PRIsVALUE, msg);
}
return Qnil;
diff --git a/proc.c b/proc.c
index f9bd469618..bb0ad89851 100644
--- a/proc.c
+++ b/proc.c
@@ -15,6 +15,7 @@
#include "internal/class.h"
#include "internal/error.h"
#include "internal/eval.h"
+#include "internal/hash.h"
#include "internal/object.h"
#include "internal/proc.h"
#include "internal/symbol.h"
@@ -37,7 +38,11 @@ const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase);
struct METHOD {
const VALUE recv;
const VALUE klass;
+ /* needed for #super_method */
const VALUE iclass;
+ /* Different than me->owner only for ZSUPER methods.
+ This is error-prone but unavoidable unless ZSUPER methods are removed. */
+ const VALUE owner;
const rb_method_entry_t * const me;
/* for bound methods, `me' should be rb_callable_method_entry_t * */
};
@@ -85,7 +90,7 @@ block_mark(const struct rb_block *block)
const struct rb_captured_block *captured = &block->as.captured;
RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self);
RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val);
- if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) {
+ if (captured->ep && !UNDEF_P(captured->ep[VM_ENV_DATA_INDEX_ENV]) /* cfunc_proc_t */) {
rb_gc_mark(VM_ENV_ENVVAL(captured->ep));
}
}
@@ -1363,7 +1368,7 @@ iseq_location(const rb_iseq_t *iseq)
if (!iseq) return Qnil;
rb_iseq_check(iseq);
loc[0] = rb_iseq_path(iseq);
- loc[1] = ISEQ_BODY(iseq)->location.first_lineno;
+ loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
return rb_ary_new4(2, loc);
}
@@ -1460,8 +1465,24 @@ rb_hash_proc(st_index_t hash, VALUE prc)
{
rb_proc_t *proc;
GetProcPtr(prc, proc);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self);
+
+ switch (vm_block_type(&proc->block)) {
+ case block_type_iseq:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.iseq->body);
+ break;
+ case block_type_ifunc:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func);
+ break;
+ case block_type_symbol:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol));
+ break;
+ case block_type_proc:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.proc));
+ break;
+ default:
+ rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block));
+ }
+
return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
}
@@ -1544,7 +1565,7 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i
const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq);
rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self,
rb_iseq_path(iseq),
- FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
+ ISEQ_BODY(iseq)->location.first_lineno);
}
break;
case block_type_symbol:
@@ -1597,6 +1618,7 @@ bm_mark(void *ptr)
rb_gc_mark_movable(data->recv);
rb_gc_mark_movable(data->klass);
rb_gc_mark_movable(data->iclass);
+ rb_gc_mark_movable(data->owner);
rb_gc_mark_movable((VALUE)data->me);
}
@@ -1607,6 +1629,7 @@ bm_compact(void *ptr)
UPDATE_REFERENCE(data->recv);
UPDATE_REFERENCE(data->klass);
UPDATE_REFERENCE(data->iclass);
+ UPDATE_REFERENCE(data->owner);
UPDATE_TYPED_REFERENCE(rb_method_entry_t *, data->me);
}
@@ -1639,7 +1662,7 @@ respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope)
/* TODO: merge with obj_respond_to() */
ID rmiss = idRespond_to_missing;
- if (obj == Qundef) return 0;
+ if (UNDEF_P(obj)) return 0;
if (rb_method_basic_definition_p(klass, rmiss)) return 0;
return RTEST(rb_funcall(obj, rmiss, 2, sym, RBOOL(!scope)));
}
@@ -1655,6 +1678,7 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
RB_OBJ_WRITE(method, &data->recv, obj);
RB_OBJ_WRITE(method, &data->klass, klass);
+ RB_OBJ_WRITE(method, &data->owner, klass);
def = ZALLOC(rb_method_definition_t);
def->type = VM_METHOD_TYPE_MISSING;
@@ -1682,8 +1706,10 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
{
struct METHOD *data;
VALUE method;
+ const rb_method_entry_t *original_me = me;
rb_method_visibility_t visi = METHOD_VISI_UNDEF;
+ again:
if (UNDEFINED_METHOD_ENTRY_P(me)) {
if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
return mnew_missing(klass, obj, id, mclass);
@@ -1699,12 +1725,32 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
rb_print_inaccessible(klass, id, visi);
}
}
+ if (me->def->type == VM_METHOD_TYPE_ZSUPER) {
+ if (me->defined_class) {
+ VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
+ id = me->def->original_id;
+ me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
+ }
+ else {
+ VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->owner));
+ id = me->def->original_id;
+ me = rb_method_entry_without_refinements(klass, id, &iclass);
+ }
+ goto again;
+ }
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
- RB_OBJ_WRITE(method, &data->recv, obj);
- RB_OBJ_WRITE(method, &data->klass, klass);
+ if (obj == Qundef) {
+ RB_OBJ_WRITE(method, &data->recv, Qundef);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
+ }
+ else {
+ RB_OBJ_WRITE(method, &data->recv, obj);
+ RB_OBJ_WRITE(method, &data->klass, klass);
+ }
RB_OBJ_WRITE(method, &data->iclass, iclass);
+ RB_OBJ_WRITE(method, &data->owner, original_me->owner);
RB_OBJ_WRITE(method, &data->me, me);
return method;
@@ -1723,7 +1769,7 @@ mnew_callable(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
const rb_method_entry_t *me;
VALUE iclass = Qnil;
- ASSUME(obj != Qundef);
+ ASSUME(!UNDEF_P(obj));
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
}
@@ -1738,27 +1784,6 @@ mnew_unbound(VALUE klass, ID id, VALUE mclass, int scope)
return mnew_from_me(me, klass, iclass, Qundef, id, mclass, scope);
}
-static const rb_method_entry_t*
-zsuper_resolve(const rb_method_entry_t *me)
-{
- const rb_method_entry_t *super_me;
- while (me->def->type == VM_METHOD_TYPE_ZSUPER) {
- VALUE defined_class = me->defined_class ? me->defined_class : me->owner;
- VALUE super_class = RCLASS_SUPER(RCLASS_ORIGIN(defined_class));
- if (!super_class) {
- break;
- }
- ID id = me->def->original_id;
- VALUE iclass;
- super_me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, id, &iclass);
- if (!super_me) {
- break;
- }
- me = super_me;
- }
- return me;
-}
-
static inline VALUE
method_entry_defined_class(const rb_method_entry_t *me)
{
@@ -1819,13 +1844,10 @@ method_eq(VALUE method, VALUE other)
m1 = (struct METHOD *)DATA_PTR(method);
m2 = (struct METHOD *)DATA_PTR(other);
- const rb_method_entry_t *m1_me = zsuper_resolve(m1->me);
- const rb_method_entry_t *m2_me = zsuper_resolve(m2->me);
-
- klass1 = method_entry_defined_class(m1_me);
- klass2 = method_entry_defined_class(m2_me);
+ klass1 = method_entry_defined_class(m1->me);
+ klass2 = method_entry_defined_class(m2->me);
- if (!rb_method_entry_eq(m1_me, m2_me) ||
+ if (!rb_method_entry_eq(m1->me, m2->me) ||
klass1 != klass2 ||
m1->klass != m2->klass ||
m1->recv != m2->recv) {
@@ -1837,6 +1859,22 @@ method_eq(VALUE method, VALUE other)
/*
* call-seq:
+ * meth.eql?(other_meth) -> true or false
+ * meth == other_meth -> true or false
+ *
+ * Two unbound method objects are equal if they refer to the same
+ * method definition.
+ *
+ * Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice)
+ * #=> true
+ *
+ * Array.instance_method(:sum) == Enumerable.instance_method(:sum)
+ * #=> false, Array redefines the method for efficiency
+ */
+#define unbound_method_eq method_eq
+
+/*
+ * call-seq:
* meth.hash -> integer
*
* Returns a hash value corresponding to the method object.
@@ -1877,8 +1915,9 @@ method_unbind(VALUE obj)
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
&method_data_type, data);
RB_OBJ_WRITE(method, &data->recv, Qundef);
- RB_OBJ_WRITE(method, &data->klass, orig->klass);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));
return method;
@@ -1963,7 +2002,7 @@ method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
- return data->me->owner;
+ return data->owner;
}
void
@@ -1988,7 +2027,7 @@ rb_method_name_error(VALUE klass, VALUE str)
else if (RB_TYPE_P(c, T_MODULE)) {
s = MSG(" module");
}
- if (s == Qundef) {
+ if (UNDEF_P(s)) {
s = MSG(" class");
}
rb_name_err_raise_str(s, c, str);
@@ -2360,17 +2399,7 @@ rb_obj_define_method(int argc, VALUE *argv, VALUE obj)
static VALUE
top_define_method(int argc, VALUE *argv, VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- VALUE klass;
-
- klass = th->top_wrapper;
- if (klass) {
- rb_warning("main.define_method in the wrapped load is effective only in wrapper module");
- }
- else {
- klass = rb_cObject;
- }
- return rb_mod_define_method(argc, argv, klass);
+ return rb_mod_define_method(argc, argv, rb_top_main_class("define_method"));
}
/*
@@ -2402,6 +2431,7 @@ method_clone(VALUE self)
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(clone, &data->owner, orig->owner);
RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
return clone;
}
@@ -2452,8 +2482,7 @@ method_clone(VALUE self)
static VALUE
rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
{
- VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
- return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS);
+ return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS);
}
VALUE
@@ -2493,7 +2522,7 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa
rb_execution_context_t *ec = GET_EC();
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- if (data->recv == Qundef) {
+ if (UNDEF_P(data->recv)) {
rb_raise(rb_eTypeError, "can't call unbound method; bind first");
}
return call_method_data(ec, data, argc, argv, passed_procval, kw_splat);
@@ -2563,7 +2592,7 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
static void
convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out, const bool clone)
{
- VALUE methclass = data->me->owner;
+ VALUE methclass = data->owner;
VALUE iclass = data->me->defined_class;
VALUE klass = CLASS_OF(recv);
@@ -2571,8 +2600,7 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
VALUE refined_class = rb_refinement_module_get_refined_class(methclass);
if (!NIL_P(refined_class)) methclass = refined_class;
}
- if (!RB_TYPE_P(methclass, T_MODULE) &&
- methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) {
+ if (!RB_TYPE_P(methclass, T_MODULE) && !RTEST(rb_obj_is_kind_of(recv, methclass))) {
if (FL_TEST(methclass, FL_SINGLETON)) {
rb_raise(rb_eTypeError,
"singleton method called for a different object");
@@ -2586,14 +2614,15 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
const rb_method_entry_t *me;
if (clone) {
me = rb_method_entry_clone(data->me);
- } else {
+ }
+ else {
me = data->me;
}
if (RB_TYPE_P(me->owner, T_MODULE)) {
if (!clone) {
// if we didn't previously clone the method entry, then we need to clone it now
- // because this branch manipualtes it in rb_method_entry_complement_defined_class
+ // because this branch manipulates it in rb_method_entry_complement_defined_class
me = rb_method_entry_clone(me);
}
VALUE ic = rb_class_search_ancestor(klass, me->owner);
@@ -2662,6 +2691,7 @@ umethod_bind(VALUE method, VALUE recv)
RB_OBJ_WRITE(method, &bound->recv, recv);
RB_OBJ_WRITE(method, &bound->klass, klass);
RB_OBJ_WRITE(method, &bound->iclass, iclass);
+ RB_OBJ_WRITE(method, &bound->owner, methclass);
RB_OBJ_WRITE(method, &bound->me, me);
return method;
@@ -2698,7 +2728,7 @@ umethod_bind_call(int argc, VALUE *argv, VALUE method)
VALUE methclass, klass, iclass;
const rb_method_entry_t *me;
convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false);
- struct METHOD bound = { recv, klass, 0, me };
+ struct METHOD bound = { recv, klass, 0, methclass, me };
return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS);
}
@@ -2969,14 +2999,6 @@ rb_method_entry_location(const rb_method_entry_t *me)
return method_def_location(me->def);
}
-static const rb_method_definition_t *
-zsuper_ref_method_def(VALUE method)
-{
- const struct METHOD *data;
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- return zsuper_resolve(data->me)->def;
-}
-
/*
* call-seq:
* meth.source_location -> [String, Integer]
@@ -2988,7 +3010,7 @@ zsuper_ref_method_def(VALUE method)
VALUE
rb_method_location(VALUE method)
{
- return method_def_location(zsuper_ref_method_def(method));
+ return method_def_location(rb_method_def(method));
}
static const rb_method_definition_t *
@@ -3076,7 +3098,7 @@ method_def_parameters(const rb_method_definition_t *def)
static VALUE
rb_method_parameters(VALUE method)
{
- return method_def_parameters(zsuper_ref_method_def(method));
+ return method_def_parameters(rb_method_def(method));
}
/*
@@ -3139,17 +3161,21 @@ method_inspect(VALUE method)
defined_class = data->me->def->body.alias.original_me->owner;
}
else {
- defined_class = method_entry_defined_class(zsuper_resolve(data->me));
+ defined_class = method_entry_defined_class(data->me);
}
if (RB_TYPE_P(defined_class, T_ICLASS)) {
defined_class = RBASIC_CLASS(defined_class);
}
- if (FL_TEST(mklass, FL_SINGLETON)) {
+ if (data->recv == Qundef) {
+ // UnboundMethod
+ rb_str_buf_append(str, rb_inspect(defined_class));
+ }
+ else if (FL_TEST(mklass, FL_SINGLETON)) {
VALUE v = rb_ivar_get(mklass, attached);
- if (data->recv == Qundef) {
+ if (UNDEF_P(data->recv)) {
rb_str_buf_append(str, rb_inspect(mklass));
}
else if (data->recv == v) {
@@ -3506,7 +3532,7 @@ proc_binding(VALUE self)
if (iseq) {
rb_iseq_check(iseq);
RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj);
- bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
+ bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno;
}
else {
RB_OBJ_WRITE(bindval, &bind->pathobj,
@@ -4331,8 +4357,8 @@ Init_Proc(void)
rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
rb_undef_alloc_func(rb_cUnboundMethod);
rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
- rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
- rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "==", unbound_method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "eql?", unbound_method_eq, 1);
rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
diff --git a/process.c b/process.c
index 57df2dc06f..c317a4cc2c 100644
--- a/process.c
+++ b/process.c
@@ -1168,7 +1168,7 @@ rb_sigwait_fd_migrate(rb_vm_t *vm)
extern volatile unsigned int ruby_nocldwait; /* signal.c */
/* called by timer thread or thread which acquired sigwait_fd */
static void
-waitpid_each(struct ccan_list_head *head)
+waitpid_each(rb_vm_t *vm, struct ccan_list_head *head)
{
struct waitpid_state *w = 0, *next;
@@ -1178,6 +1178,18 @@ waitpid_each(struct ccan_list_head *head)
if (!ret) continue;
if (ret == -1) w->errnum = errno;
+ if (w->pid <= 0) {
+ /* when waiting for a group of processes, make sure a waiter for a
+ * specific pid is given that event in preference */
+ struct waitpid_state *w_inner = 0, *next_inner;
+ ccan_list_for_each_safe(&vm->waiting_pids, w_inner, next_inner, wnode) {
+ if (w_inner->pid == ret) {
+ /* signal this one instead */
+ w = w_inner;
+ }
+ }
+ }
+
w->ret = ret;
ccan_list_del_init(&w->wnode);
waitpid_signal(w);
@@ -1192,10 +1204,8 @@ ruby_waitpid_all(rb_vm_t *vm)
{
#if RUBY_SIGCHLD
rb_native_mutex_lock(&vm->waitpid_lock);
- waitpid_each(&vm->waiting_pids);
- if (ccan_list_empty(&vm->waiting_pids)) {
- waitpid_each(&vm->waiting_grps);
- }
+ waitpid_each(vm, &vm->waiting_pids);
+ waitpid_each(vm, &vm->waiting_grps);
/* emulate SA_NOCLDWAIT */
if (ccan_list_empty(&vm->waiting_pids) && ccan_list_empty(&vm->waiting_grps)) {
while (ruby_nocldwait && do_waitpid(-1, 0, WNOHANG) > 0)
@@ -1260,6 +1270,7 @@ waitpid_cleanup(VALUE x)
return Qfalse;
}
+#if RUBY_SIGCHLD
static void
waitpid_wait(struct waitpid_state *w)
{
@@ -1273,10 +1284,17 @@ waitpid_wait(struct waitpid_state *w)
*/
rb_native_mutex_lock(&vm->waitpid_lock);
- if (w->pid > 0 || ccan_list_empty(&vm->waiting_pids)) {
- w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+ if (w->options & WNOHANG && w->pid <= 0) {
+ /* In the case of WNOHANG wait for a group, make sure there isn't a zombie child
+ * whose PID we are directly waiting for in another call to waitpid. If there is,
+ * we will reap it via a call to waitpid($pid) with this call to waitpid_each. */
+ waitpid_each(vm, &vm->waiting_pids);
+ /* _now_ it's safe to call do_waitpid, without risk of stealing the wait from
+ * another directed call. */
}
+ w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+
if (w->ret) {
if (w->ret == -1) w->errnum = errno;
}
@@ -1298,6 +1316,7 @@ waitpid_wait(struct waitpid_state *w)
rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w);
}
}
+#endif
static void *
waitpid_blocking_no_SIGCHLD(void *x)
@@ -1332,7 +1351,7 @@ rb_process_status_wait(rb_pid_t pid, int flags)
if (!(flags & WNOHANG)) {
VALUE scheduler = rb_fiber_scheduler_current();
VALUE result = rb_fiber_scheduler_process_wait(scheduler, pid, flags);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
}
struct waitpid_state waitpid_state;
@@ -1340,12 +1359,11 @@ rb_process_status_wait(rb_pid_t pid, int flags)
waitpid_state_init(&waitpid_state, pid, flags);
waitpid_state.ec = GET_EC();
- if (WAITPID_USE_SIGCHLD) {
- waitpid_wait(&waitpid_state);
- }
- else {
- waitpid_no_SIGCHLD(&waitpid_state);
- }
+#if WAITPID_USE_SIGCHLD
+ waitpid_wait(&waitpid_state);
+#else
+ waitpid_no_SIGCHLD(&waitpid_state);
+#endif
if (waitpid_state.ret == 0) return Qnil;
@@ -1426,7 +1444,7 @@ rb_waitpid(rb_pid_t pid, int *st, int flags)
VALUE status = rb_process_status_wait(pid, flags);
if (NIL_P(status)) return 0;
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
pid = data->pid;
if (st) *st = data->status;
@@ -1776,7 +1794,6 @@ before_fork_ruby(void)
static void
after_fork_ruby(void)
{
- rb_threadptr_pending_interrupt_clear(GET_THREAD());
after_exec();
}
#endif
@@ -3144,9 +3161,10 @@ NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
* If the string from the first form (<code>exec("command")</code>) follows
* these simple rules:
*
- * * no meta characters
- * * not starting with shell reserved word or special built-in
- * * Ruby invokes the command directly without shell
+ * * no meta characters,
+ * * not starting with shell reserved word or special built-in,
+ *
+ * Ruby invokes the command directly without shell.
*
* You can force shell invocation by adding ";" to the string (because ";" is
* a meta character).
@@ -4204,6 +4222,30 @@ retry_fork_async_signal_safe(struct rb_process_status *status, int *ep,
}
}
+#if USE_MJIT
+// This is used to create MJIT's child Ruby process
+pid_t
+rb_mjit_fork(void)
+{
+ struct child_handler_disabler_state old;
+ rb_vm_t *vm = GET_VM();
+ prefork();
+ disable_child_handler_before_fork(&old);
+ before_fork_ruby();
+
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ pid_t pid = rb_fork();
+ if (pid > 0) mjit_add_waiting_pid(vm, pid);
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+
+ after_fork_ruby();
+ disable_child_handler_fork_parent(&old);
+ if (pid == 0) rb_thread_atfork();
+
+ return pid;
+}
+#endif
+
static rb_pid_t
fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, size_t), void *charg,
VALUE fds, char *errmsg, size_t errmsg_buflen,
@@ -4818,7 +4860,8 @@ rb_f_system(int argc, VALUE *argv, VALUE _)
if (pid > 0) {
VALUE status = rb_process_status_wait(pid, 0);
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
// Set the last status:
rb_obj_freeze(status);
@@ -5524,6 +5567,9 @@ rlimit_resource_name2int(const char *name, long len, int casetype)
#ifdef RLIMIT_NPROC
RESCHECK(NPROC);
#endif
+#ifdef RLIMIT_NPTS
+ RESCHECK(NPTS);
+#endif
#ifdef RLIMIT_NICE
RESCHECK(NICE);
#endif
@@ -5752,6 +5798,7 @@ proc_getrlimit(VALUE obj, VALUE resource)
* [NICE] ceiling on process's nice(2) value (number) (GNU/Linux)
* [NOFILE] file descriptors (number) (SUSv3)
* [NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux)
+ * [NPTS] number of pseudo terminals (number) (FreeBSD)
* [RSS] resident memory size (bytes) (4.2BSD, GNU/Linux)
* [RTPRIO] ceiling on the process's real-time priority (number) (GNU/Linux)
* [RTTIME] CPU time for real-time process (us) (GNU/Linux)
@@ -7085,19 +7132,14 @@ rb_daemon(int nochdir, int noclose)
#else
int n;
-#define fork_daemon() \
- switch (rb_fork_ruby(NULL)) { \
- case -1: return -1; \
- case 0: break; \
- default: _exit(EXIT_SUCCESS); \
+ switch (rb_fork_ruby(NULL)) {
+ case -1: return -1;
+ case 0: break;
+ default: _exit(EXIT_SUCCESS);
}
- fork_daemon();
-
- if (setsid() < 0) return -1;
-
- /* must not be process-leader */
- fork_daemon();
+ /* ignore EPERM which means already being process-leader */
+ if (setsid() < 0) (void)0;
if (!nochdir)
err = chdir("/");
@@ -8337,7 +8379,9 @@ rb_clock_gettime(int argc, VALUE *argv, VALUE _)
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
+#ifdef HAVE_CLOCK_GETTIME
clockid_t c;
+#endif
if (SYMBOL_P(clk_id)) {
#ifdef CLOCK_REALTIME
@@ -8563,7 +8607,9 @@ rb_clock_getres(int argc, VALUE *argv, VALUE _)
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
+#ifdef HAVE_CLOCK_GETRES
clockid_t c;
+#endif
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
@@ -8703,7 +8749,7 @@ get_PROCESS_ID(ID _x, VALUE *_y)
/*
* call-seq:
- * Process.kill(signal, pid, ...) -> integer
+ * Process.kill(signal, pid, *pids) -> integer
*
* Sends the given signal to the specified process id(s) if _pid_ is positive.
* If _pid_ is zero, _signal_ is sent to all processes whose group ID is equal
@@ -8956,6 +9002,14 @@ InitVM_process(void)
*/
rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
#endif
+#ifdef RLIMIT_NPTS
+ /* The maximum number of pseudo-terminals that can be created for the
+ * real user ID of the calling process.
+ *
+ * see the system getrlimit(2) manual for details.
+ */
+ rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
+#endif
#ifdef RLIMIT_RSS
/* Specifies the limit (in pages) of the process's resident set.
*
diff --git a/ractor.c b/ractor.c
index 0306736c18..8fb563fa11 100644
--- a/ractor.c
+++ b/ractor.c
@@ -17,6 +17,7 @@
#include "gc.h"
#include "transient_heap.h"
#include "yjit.h"
+#include "mjit.h"
VALUE rb_cRactor;
@@ -74,7 +75,9 @@ static void
ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
+#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by != cr->pub.self);
+#endif
ractor_lock(cr, file, line);
}
@@ -94,7 +97,9 @@ static void
ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
+#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by == cr->pub.self);
+#endif
ractor_unlock(cr, file, line);
}
@@ -724,7 +729,7 @@ ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
VALUE v;
- while ((v = ractor_try_receive(ec, cr)) == Qundef) {
+ while (UNDEF_P(v = ractor_try_receive(ec, cr))) {
ractor_receive_wait(ec, cr);
}
@@ -871,7 +876,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
}
RACTOR_UNLOCK_SELF(cr);
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
struct receive_block_data data = {
.cr = cr,
.rq = rq,
@@ -883,7 +888,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
receive_if_ensure, (VALUE)&data);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
index++;
}
@@ -1091,7 +1096,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
int i;
bool interrupted = false;
enum rb_ractor_wait_status wait_status = 0;
- bool yield_p = (yielded_value != Qundef) ? true : false;
+ bool yield_p = !UNDEF_P(yielded_value) ? true : false;
const int alen = rs_len + (yield_p ? 1 : 0);
struct ractor_select_action {
@@ -1148,7 +1153,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
case ractor_select_action_take:
rv = actions[i].v;
v = ractor_try_take(ec, RACTOR_PTR(rv));
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
*ret_r = rv;
ret = v;
goto cleanup;
@@ -1156,7 +1161,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
break;
case ractor_select_action_receive:
v = ractor_try_receive(ec, cr);
- if (v != Qundef) {
+ if (!UNDEF_P(v)) {
*ret_r = ID2SYM(rb_intern("receive"));
ret = v;
goto cleanup;
@@ -1320,7 +1325,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
goto restart;
}
- VM_ASSERT(ret != Qundef);
+ VM_ASSERT(!UNDEF_P(ret));
return ret;
}
@@ -1434,12 +1439,6 @@ cancel_single_ractor_mode(void)
}
ruby_single_main_ractor = NULL;
-
- if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
- rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
- "Ractor is experimental, and the behavior may change in future versions of Ruby! "
- "Also there are many implementation issues.");
- }
}
static void
@@ -1608,6 +1607,7 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->debug = cr->debug;
rb_yjit_before_ractor_spawn();
+ rb_mjit_before_ractor_spawn();
rb_thread_create_ractor(r, args, block);
RB_GC_GUARD(rv);
@@ -2250,6 +2250,19 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
return ST_CONTINUE;
}
+static int
+obj_hash_iv_traverse_i(st_data_t key, st_data_t val, st_data_t ptr)
+{
+ struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
+
+ if (obj_traverse_i((VALUE)val, d->data)) {
+ d->stop = true;
+ return ST_STOP;
+ }
+
+ return ST_CONTINUE;
+}
+
static void
obj_traverse_reachable_i(VALUE obj, void *ptr)
{
@@ -2291,7 +2304,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) {
VALUE val = ivtbl->ivptr[i];
- if (val != Qundef && obj_traverse_i(val, data)) return 1;
+ if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
}
}
@@ -2308,12 +2321,22 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
case T_OBJECT:
{
- uint32_t len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
+ if (rb_shape_obj_too_complex(obj)) {
+ struct obj_traverse_callback_data d = {
+ .stop = false,
+ .data = data,
+ };
+ rb_st_foreach(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, (st_data_t)&d);
+ if (d.stop) return 1;
+ }
+ else {
+ uint32_t len = ROBJECT_IV_COUNT(obj);
+ VALUE *ptr = ROBJECT_IVPTR(obj);
- for (uint32_t i=0; i<len; i++) {
- VALUE val = ptr[i];
- if (val != Qundef && obj_traverse_i(val, data)) return 1;
+ for (uint32_t i=0; i<len; i++) {
+ VALUE val = ptr[i];
+ if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
+ }
}
}
break;
@@ -2658,6 +2681,30 @@ obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int e
return ST_CONTINUE;
}
+static int
+obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
+{
+ return ST_REPLACE;
+}
+
+static int
+obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
+{
+ struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
+ struct obj_traverse_replace_data *data = d->data;
+
+ if (obj_traverse_replace_i(*(VALUE *)val, data)) {
+ d->stop = true;
+ return ST_STOP;
+ }
+ else if (*(VALUE *)val != data->replacement) {
+ VALUE v = *(VALUE *)val = data->replacement;
+ RB_OBJ_WRITTEN(d->src, Qundef, v);
+ }
+
+ return ST_CONTINUE;
+}
+
static struct st_table *
obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
{
@@ -2681,7 +2728,7 @@ obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
int *pcnt = (int *)ptr;
if (!rb_ractor_shareable_p(obj)) {
- pcnt++;
+ *pcnt++;
}
}
@@ -2737,7 +2784,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) {
- if (ivtbl->ivptr[i] != Qundef) {
+ if (!UNDEF_P(ivtbl->ivptr[i])) {
CHECK_AND_REPLACE(ivtbl->ivptr[i]);
}
}
@@ -2758,16 +2805,31 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_OBJECT:
{
+ if (rb_shape_obj_too_complex(obj)) {
+ st_table * table = ROBJECT_IV_HASH(obj);
+ struct obj_traverse_replace_callback_data d = {
+ .stop = false,
+ .data = data,
+ .src = obj,
+ };
+ rb_st_foreach_with_replace(
+ table,
+ obj_iv_hash_traverse_replace_foreach_i,
+ obj_iv_hash_traverse_replace_i,
+ (st_data_t)&d);
+ }
+ else {
#if USE_TRANSIENT_HEAP
- if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
+ if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
#endif
- uint32_t len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
+ uint32_t len = ROBJECT_IV_COUNT(obj);
+ VALUE *ptr = ROBJECT_IVPTR(obj);
- for (uint32_t i=0; i<len; i++) {
- if (ptr[i] != Qundef) {
- CHECK_AND_REPLACE(ptr[i]);
+ for (uint32_t i=0; i<len; i++) {
+ if (!UNDEF_P(ptr[i])) {
+ CHECK_AND_REPLACE(ptr[i]);
+ }
}
}
}
@@ -2971,7 +3033,7 @@ static VALUE
ractor_move(VALUE obj)
{
VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
- if (val != Qundef) {
+ if (!UNDEF_P(val)) {
return val;
}
else {
@@ -3002,7 +3064,7 @@ static VALUE
ractor_copy(VALUE obj)
{
VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
- if (val != Qundef) {
+ if (!UNDEF_P(val)) {
return val;
}
else {
@@ -3134,7 +3196,7 @@ static bool
ractor_local_ref(rb_ractor_local_key_t key, void **pret)
{
if (rb_ractor_main_p()) {
- if ((VALUE)key->main_cache != Qundef) {
+ if (!UNDEF_P((VALUE)key->main_cache)) {
*pret = key->main_cache;
return true;
}
diff --git a/ractor.rb b/ractor.rb
index 953d3ceddc..1031fe499b 100644
--- a/ractor.rb
+++ b/ractor.rb
@@ -1,4 +1,4 @@
-# Ractor is a Actor-model abstraction for Ruby that provides thread-safe parallel execution.
+# Ractor is an Actor-model abstraction for Ruby that provides thread-safe parallel execution.
#
# Ractor.new can make a new Ractor, and it will run in parallel.
#
@@ -262,6 +262,10 @@ class Ractor
def self.new(*args, name: nil, &block)
b = block # TODO: builtin bug
raise ArgumentError, "must be called with a block" unless block
+ if __builtin_cexpr!("RBOOL(ruby_single_main_ractor)")
+ warn("Ractor is experimental, and the behavior may change in future versions of Ruby! " \
+ "Also there are many implementation issues.", uplevel: 0, category: :experimental)
+ end
loc = caller_locations(1, 1).first
loc = "#{loc.path}:#{loc.lineno}"
__builtin_ractor_create(loc, name, args, b)
diff --git a/ractor_core.h b/ractor_core.h
index 412971decf..968c12d291 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -5,7 +5,7 @@
#include "vm_debug.h"
#ifndef RACTOR_CHECK_MODE
-#define RACTOR_CHECK_MODE (0 || VM_CHECK_MODE || RUBY_DEBUG)
+#define RACTOR_CHECK_MODE (VM_CHECK_MODE || RUBY_DEBUG) && (SIZEOF_UINT64_T == SIZEOF_VALUE)
#endif
enum rb_ractor_basket_type {
@@ -288,13 +288,14 @@ rb_ractor_id(const rb_ractor_t *r)
}
#if RACTOR_CHECK_MODE > 0
+# define RACTOR_BELONGING_ID(obj) (*(uint32_t *)(((uintptr_t)(obj)) + rb_gc_obj_slot_size(obj)))
+
uint32_t rb_ractor_current_id(void);
static inline void
rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid)
{
- VALUE flags = RBASIC(obj)->flags & 0xffffffff; // 4B
- RBASIC(obj)->flags = flags | ((VALUE)rid << 32);
+ RACTOR_BELONGING_ID(obj) = rid;
}
static inline void
@@ -310,7 +311,7 @@ rb_ractor_belonging(VALUE obj)
return 0;
}
else {
- return RBASIC(obj)->flags >> 32;
+ return RACTOR_BELONGING_ID(obj);
}
}
diff --git a/random.c b/random.c
index 9476de0d4a..4b5b7ab6c4 100644
--- a/random.c
+++ b/random.c
@@ -371,11 +371,14 @@ rand_init(const rb_random_interface_t *rng, rb_random_t *rnd, VALUE seed)
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
if (sign < 0)
sign = -sign;
- if (len > 1) {
+ if (len <= 1) {
+ rng->init_int32(rnd, len ? buf[0] : 0);
+ }
+ else {
if (sign != 2 && buf[len-1] == 1) /* remove leading-zero-guard */
len--;
+ rng->init(rnd, buf, len);
}
- rng->init(rnd, buf, len);
explicit_bzero(buf, len * sizeof(*buf));
ALLOCV_END(buf0);
return seed;
@@ -400,6 +403,15 @@ random_init(int argc, VALUE *argv, VALUE obj)
rb_raise(rb_eTypeError, "undefined random interface: %s",
RTYPEDDATA_TYPE(obj)->wrap_struct_name);
}
+
+ unsigned int major = rng->version.major;
+ unsigned int minor = rng->version.minor;
+ if (major != RUBY_RANDOM_INTERFACE_VERSION_MAJOR) {
+ rb_raise(rb_eTypeError, "Random interface version "
+ STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MAJOR) "."
+ STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MINOR) " "
+ "expected: %d.%d", major, minor);
+ }
argc = rb_check_arity(argc, 0, 1);
rb_check_frozen(obj);
if (argc == 0) {
@@ -487,28 +499,35 @@ fill_random_bytes_urandom(void *seed, size_t size)
#if 0
#elif defined MAC_OS_X_VERSION_10_7 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
-# if defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+# if defined(USE_COMMON_RANDOM)
+# elif defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+# define USE_COMMON_RANDOM 1
+# else
+# define USE_COMMON_RANDOM 0
+# endif
+# if USE_COMMON_RANDOM
# include <CommonCrypto/CommonCryptoError.h> /* for old Xcode */
# include <CommonCrypto/CommonRandom.h>
-# define USE_COMMON_RANDOM 1
# else
# include <Security/SecRandom.h>
-# define USE_COMMON_RANDOM 0
# endif
static int
fill_random_bytes_syscall(void *seed, size_t size, int unused)
{
#if USE_COMMON_RANDOM
- int failed = CCRandomGenerateBytes(seed, size) != kCCSuccess;
+ CCRNGStatus status = CCRandomGenerateBytes(seed, size);
+ int failed = status != kCCSuccess;
#else
- int failed = SecRandomCopyBytes(kSecRandomDefault, size, seed) != errSecSuccess;
+ int status = SecRandomCopyBytes(kSecRandomDefault, size, seed);
+ int failed = status != errSecSuccess;
#endif
if (failed) {
# if 0
# if USE_COMMON_RANDOM
/* How to get the error message? */
+ fprintf(stderr, "CCRandomGenerateBytes failed: %d\n", status);
# else
CFStringRef s = SecCopyErrorMessageString(status, NULL);
const char *m = s ? CFStringGetCStringPtr(s, kCFStringEncodingUTF8) : NULL;
@@ -876,15 +895,17 @@ rand_mt_load(VALUE obj, VALUE dump)
}
static void
+rand_mt_init_int32(rb_random_t *rnd, uint32_t data)
+{
+ struct MT *mt = &((rb_random_mt_t *)rnd)->mt;
+ init_genrand(mt, data);
+}
+
+static void
rand_mt_init(rb_random_t *rnd, const uint32_t *buf, size_t len)
{
struct MT *mt = &((rb_random_mt_t *)rnd)->mt;
- if (len <= 1) {
- init_genrand(mt, len ? buf[0] : 0);
- }
- else {
- init_by_array(mt, buf, (int)len);
- }
+ init_by_array(mt, buf, (int)len);
}
static unsigned int
@@ -1782,6 +1803,9 @@ rb_reset_random_seed(void)
* PRNGs are currently implemented as a modified Mersenne Twister with a period
* of 2**19937-1. As this algorithm is _not_ for cryptographical use, you must
* use SecureRandom for security purpose, instead of this PRNG.
+ *
+ * See also Random::Formatter module that adds convenience methods to generate
+ * various forms of random data.
*/
void
diff --git a/range.c b/range.c
index 39786ed97c..dbc37c1410 100644
--- a/range.c
+++ b/range.c
@@ -532,7 +532,11 @@ range_step(int argc, VALUE *argv, VALUE range)
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(b));
}
- range_each_func(range, step_i, (VALUE)iter);
+ if (!NIL_P(e))
+ range_each_func(range, step_i, (VALUE)iter);
+ else
+ for (;; b = rb_funcallv(b, id_succ, 0, 0))
+ step_i(b, (VALUE)iter);
}
}
return range;
@@ -607,7 +611,7 @@ is_integer_p(VALUE v)
VALUE is_int;
CONST_ID(id_integer_p, "integer?");
is_int = rb_check_funcall(v, id_integer_p, 0, 0);
- return RTEST(is_int) && is_int != Qundef;
+ return RTEST(is_int) && !UNDEF_P(is_int);
}
static VALUE
@@ -816,7 +820,9 @@ range_size(VALUE range)
}
}
else if (NIL_P(b)) {
- return DBL2NUM(HUGE_VAL);
+ if (rb_obj_is_kind_of(e, rb_cNumeric)) {
+ return DBL2NUM(HUGE_VAL);
+ }
}
return Qnil;
@@ -1107,10 +1113,6 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
x = EXCL(range);
len_1 = rb_int_minus(e, b);
- if (FIXNUM_ZERO_P(len_1) || rb_num_negative_p(len_1)) {
- return rb_ary_new_capa(0);
- }
-
if (x) {
e = rb_int_minus(e, ONE);
len = len_1;
@@ -1119,6 +1121,10 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
len = rb_int_plus(len_1, ONE);
}
+ if (FIXNUM_ZERO_P(len) || rb_num_negative_p(len)) {
+ return rb_ary_new_capa(0);
+ }
+
rb_scan_args(argc, argv, "1", &nv);
n = NUM2LONG(nv);
if (n < 0) {
@@ -1295,10 +1301,9 @@ range_min(int argc, VALUE *argv, VALUE range)
return range_first(argc, argv, range);
}
else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE b = RANGE_BEG(range);
VALUE e = RANGE_END(range);
- int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);
+ int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e);
if (c > 0 || (c == 0 && EXCL(range)))
return Qnil;
@@ -1406,8 +1411,7 @@ range_max(int argc, VALUE *argv, VALUE range)
return rb_call_super(argc, argv);
}
else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
- int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);
+ int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e);
if (c > 0)
return Qnil;
@@ -1503,11 +1507,11 @@ rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
else {
VALUE x;
b = rb_check_funcall(range, id_beg, 0, 0);
- if (b == Qundef) return (int)Qfalse;
+ if (UNDEF_P(b)) return (int)Qfalse;
e = rb_check_funcall(range, id_end, 0, 0);
- if (e == Qundef) return (int)Qfalse;
+ if (UNDEF_P(e)) return (int)Qfalse;
x = rb_check_funcall(range, rb_intern("exclude_end?"), 0, 0);
- if (x == Qundef) return (int)Qfalse;
+ if (UNDEF_P(x)) return (int)Qfalse;
excl = RTEST(x);
}
*begp = b;
@@ -1644,7 +1648,7 @@ inspect_range(VALUE range, VALUE dummy, int recur)
if (NIL_P(RANGE_BEG(range)) || !NIL_P(RANGE_END(range))) {
str2 = rb_inspect(RANGE_END(range));
}
- if (str2 != Qundef) rb_str_append(str, str2);
+ if (!UNDEF_P(str2)) rb_str_append(str, str2);
return str;
}
@@ -1677,7 +1681,9 @@ range_inspect(VALUE range)
return rb_exec_recursive(inspect_range, range, 0);
}
-static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover);
+static VALUE range_include_internal(VALUE range, VALUE val);
+static VALUE range_string_cover_internal(VALUE range, VALUE val);
+VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
/*
* call-seq:
@@ -1721,8 +1727,8 @@ static VALUE range_include_internal(VALUE range, VALUE val, int string_use_cover
static VALUE
range_eqq(VALUE range, VALUE val)
{
- VALUE ret = range_include_internal(range, val, 1);
- if (ret != Qundef) return ret;
+ VALUE ret = range_string_cover_internal(range, val);
+ if (!UNDEF_P(ret)) return ret;
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
@@ -1761,35 +1767,61 @@ range_eqq(VALUE range, VALUE val)
static VALUE
range_include(VALUE range, VALUE val)
{
- VALUE ret = range_include_internal(range, val, 0);
- if (ret != Qundef) return ret;
+ VALUE ret = range_include_internal(range, val);
+ if (!UNDEF_P(ret)) return ret;
return rb_call_super(1, &val);
}
+static inline bool
+range_integer_edge_p(VALUE beg, VALUE end)
+{
+ return (!NIL_P(rb_check_to_integer(beg, "to_int")) ||
+ !NIL_P(rb_check_to_integer(end, "to_int")));
+}
+
+static inline bool
+range_string_edge_p(VALUE beg, VALUE end)
+{
+ return RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING);
+}
+
+static inline bool
+range_string_range_p(VALUE beg, VALUE end)
+{
+ return RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING);
+}
+
+static inline VALUE
+range_include_fallback(VALUE beg, VALUE end, VALUE val)
+{
+ if (NIL_P(beg) && NIL_P(end)) {
+ if (linear_object_p(val)) return Qtrue;
+ }
+
+ if (NIL_P(beg) || NIL_P(end)) {
+ rb_raise(rb_eTypeError, "cannot determine inclusion in beginless/endless ranges");
+ }
+
+ return Qundef;
+}
+
static VALUE
-range_include_internal(VALUE range, VALUE val, int string_use_cover)
+range_string_cover_internal(VALUE range, VALUE val)
{
VALUE beg = RANGE_BEG(range);
VALUE end = RANGE_END(range);
int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
linear_object_p(beg) || linear_object_p(end);
- if (nv ||
- !NIL_P(rb_check_to_integer(beg, "to_int")) ||
- !NIL_P(rb_check_to_integer(end, "to_int"))) {
+ if (nv || range_integer_edge_p(beg, end)) {
return r_cover_p(range, beg, end, val);
}
- else if (RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING)) {
- if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) {
- if (string_use_cover) {
- return r_cover_p(range, beg, end, val);
- }
- else {
- VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
- return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
- }
+ else if (range_string_edge_p(beg, end)) {
+ if (range_string_range_p(beg, end)) {
+ return r_cover_p(range, beg, end, val);
}
- else if (NIL_P(beg)) {
+ if (NIL_P(beg)) {
+unbounded_begin:;
VALUE r = rb_funcall(val, id_cmp, 1, end);
if (NIL_P(r)) return Qfalse;
if (RANGE_EXCL(range)) {
@@ -1798,12 +1830,39 @@ range_include_internal(VALUE range, VALUE val, int string_use_cover)
return RBOOL(rb_cmpint(r, val, end) <= 0);
}
else if (NIL_P(end)) {
+unbounded_end:;
VALUE r = rb_funcall(beg, id_cmp, 1, val);
if (NIL_P(r)) return Qfalse;
return RBOOL(rb_cmpint(r, beg, val) <= 0);
}
}
- return Qundef;
+
+ if (!NIL_P(beg) && NIL_P(end)) {
+ goto unbounded_end;
+ }
+ if (NIL_P(beg) && !NIL_P(end)) {
+ goto unbounded_begin;
+ }
+
+ return range_include_fallback(beg, end, val);
+}
+
+static VALUE
+range_include_internal(VALUE range, VALUE val)
+{
+ VALUE beg = RANGE_BEG(range);
+ VALUE end = RANGE_END(range);
+ int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
+ linear_object_p(beg) || linear_object_p(end);
+
+ if (nv || range_integer_edge_p(beg, end)) {
+ return r_cover_p(range, beg, end, val);
+ }
+ else if (range_string_range_p(beg, end)) {
+ return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
+ }
+
+ return range_include_fallback(beg, end, val);
}
static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
@@ -1888,48 +1947,48 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* - An internal call to <tt><=></tt> returns +nil+;
* that is, the operands are not comparable.
*
- * Beginless ranges cover all values of the same type before the end,
- * excluding the end for exclusive ranges. Beginless ranges cover
- * ranges that end before the end of the beginless range, or at the
- * end of the beginless range for inclusive ranges.
- *
- * (..2).cover?(1) # => true
- * (..2).cover?(2) # => true
- * (..2).cover?(3) # => false
- * (...2).cover?(2) # => false
- * (..2).cover?("2") # => false
- * (..2).cover?(..2) # => true
- * (..2).cover?(...2) # => true
- * (..2).cover?(.."2") # => false
- * (...2).cover?(..2) # => false
- *
- * Endless ranges cover all values of the same type after the
- * beginning. Endless exclusive ranges do not cover endless
- * inclusive ranges.
- *
- * (2..).cover?(1) # => false
- * (2..).cover?(3) # => true
- * (2...).cover?(3) # => true
- * (2..).cover?(2) # => true
- * (2..).cover?("2") # => false
- * (2..).cover?(2..) # => true
- * (2..).cover?(2...) # => true
- * (2..).cover?("2"..) # => false
- * (2...).cover?(2..) # => false
- * (2...).cover?(3...) # => true
- * (2...).cover?(3..) # => false
- * (3..).cover?(2..) # => false
- *
- * Ranges that are both beginless and endless cover all values and
- * ranges, and return true for all arguments, with the exception that
- * beginless and endless exclusive ranges do not cover endless
- * inclusive ranges.
- *
- * (nil...).cover?(Object.new) # => true
- * (nil...).cover?(nil...) # => true
- * (nil..).cover?(nil...) # => true
- * (nil...).cover?(nil..) # => false
- * (nil...).cover?(1..) # => false
+ * Beginless ranges cover all values of the same type before the end,
+ * excluding the end for exclusive ranges. Beginless ranges cover
+ * ranges that end before the end of the beginless range, or at the
+ * end of the beginless range for inclusive ranges.
+ *
+ * (..2).cover?(1) # => true
+ * (..2).cover?(2) # => true
+ * (..2).cover?(3) # => false
+ * (...2).cover?(2) # => false
+ * (..2).cover?("2") # => false
+ * (..2).cover?(..2) # => true
+ * (..2).cover?(...2) # => true
+ * (..2).cover?(.."2") # => false
+ * (...2).cover?(..2) # => false
+ *
+ * Endless ranges cover all values of the same type after the
+ * beginning. Endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ * (2..).cover?(1) # => false
+ * (2..).cover?(3) # => true
+ * (2...).cover?(3) # => true
+ * (2..).cover?(2) # => true
+ * (2..).cover?("2") # => false
+ * (2..).cover?(2..) # => true
+ * (2..).cover?(2...) # => true
+ * (2..).cover?("2"..) # => false
+ * (2...).cover?(2..) # => false
+ * (2...).cover?(3...) # => true
+ * (2...).cover?(3..) # => false
+ * (3..).cover?(2..) # => false
+ *
+ * Ranges that are both beginless and endless cover all values and
+ * ranges, and return true for all arguments, with the exception that
+ * beginless and endless exclusive ranges do not cover endless
+ * inclusive ranges.
+ *
+ * (nil...).cover?(Object.new) # => true
+ * (nil...).cover?(nil...) # => true
+ * (nil..).cover?(nil...) # => true
+ * (nil...).cover?(nil..) # => false
+ * (nil...).cover?(1..) # => false
*
* Related: Range#include?.
*
diff --git a/rational.c b/rational.c
index e537bd498b..dfe2ad74eb 100644
--- a/rational.c
+++ b/rational.c
@@ -2061,30 +2061,6 @@ rb_rational_canonicalize(VALUE x)
/*
* call-seq:
- * int.numerator -> self
- *
- * Returns self.
- */
-static VALUE
-integer_numerator(VALUE self)
-{
- return self;
-}
-
-/*
- * call-seq:
- * int.denominator -> 1
- *
- * Returns 1.
- */
-static VALUE
-integer_denominator(VALUE self)
-{
- return INT2FIX(1);
-}
-
-/*
- * call-seq:
* flo.numerator -> integer
*
* Returns the numerator. The result is machine dependent.
@@ -2576,7 +2552,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
VALUE a1 = numv, a2 = denv;
int state;
- assert(a1 != Qundef);
+ assert(!UNDEF_P(a1));
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
@@ -2627,7 +2603,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
a2 = string_to_r_strict(a2, raise);
if (!raise && NIL_P(a2)) return Qnil;
}
- else if (a2 != Qundef && !rb_respond_to(a2, idTo_r)) {
+ else if (!UNDEF_P(a2) && !rb_respond_to(a2, idTo_r)) {
VALUE tmp = rb_protect(rb_check_to_int, a2, NULL);
rb_set_errinfo(Qnil);
if (!NIL_P(tmp)) {
@@ -2636,11 +2612,11 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
}
if (RB_TYPE_P(a1, T_RATIONAL)) {
- if (a2 == Qundef || (k_exact_one_p(a2)))
+ if (UNDEF_P(a2) || (k_exact_one_p(a2)))
return a1;
}
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
if (!RB_INTEGER_TYPE_P(a1)) {
if (!raise) {
VALUE result = rb_protect(to_rational, a1, NULL);
@@ -2690,7 +2666,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
a1 = nurat_int_value(a1);
- if (a2 == Qundef) {
+ if (UNDEF_P(a2)) {
a2 = ONE;
}
else if (!k_integer_p(a2) && !raise) {
@@ -2832,9 +2808,6 @@ Init_Rational(void)
rb_define_method(rb_cNumeric, "denominator", numeric_denominator, 0);
rb_define_method(rb_cNumeric, "quo", rb_numeric_quo, 1);
- rb_define_method(rb_cInteger, "numerator", integer_numerator, 0);
- rb_define_method(rb_cInteger, "denominator", integer_denominator, 0);
-
rb_define_method(rb_cFloat, "numerator", rb_float_numerator, 0);
rb_define_method(rb_cFloat, "denominator", rb_float_denominator, 0);
diff --git a/re.c b/re.c
index 5091f9a124..f357d38c63 100644
--- a/re.c
+++ b/re.c
@@ -16,6 +16,7 @@
#include "encindex.h"
#include "hrtime.h"
#include "internal.h"
+#include "internal/encoding.h"
#include "internal/hash.h"
#include "internal/imemo.h"
#include "internal/re.h"
@@ -221,10 +222,9 @@ rb_memsearch_qs_utf8(const unsigned char *xs, long m, const unsigned char *ys, l
}
static inline long
-rb_memsearch_wchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+rb_memsearch_with_char_size(const unsigned char *xs, long m, const unsigned char *ys, long n, int char_size)
{
const unsigned char *x = xs, x0 = *xs, *y = ys;
- enum {char_size = 2};
for (n -= m; n >= 0; n -= char_size, y += char_size) {
if (x0 == *y && memcmp(x+1, y+1, m-1) == 0)
@@ -234,16 +234,15 @@ rb_memsearch_wchar(const unsigned char *xs, long m, const unsigned char *ys, lon
}
static inline long
-rb_memsearch_qchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+rb_memsearch_wchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
{
- const unsigned char *x = xs, x0 = *xs, *y = ys;
- enum {char_size = 4};
+ return rb_memsearch_with_char_size(xs, m, ys, n, 2);
+}
- for (n -= m; n >= 0; n -= char_size, y += char_size) {
- if (x0 == *y && memcmp(x+1, y+1, m-1) == 0)
- return y - ys;
- }
- return -1;
+static inline long
+rb_memsearch_qchar(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+ return rb_memsearch_with_char_size(xs, m, ys, n, 4);
}
long
@@ -1068,12 +1067,13 @@ update_char_offset(VALUE match)
}
}
-static void
+static VALUE
match_check(VALUE match)
{
if (!RMATCH(match)->regexp) {
rb_raise(rb_eTypeError, "uninitialized MatchData");
}
+ return match;
}
/* :nodoc: */
@@ -1740,6 +1740,13 @@ rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_back
if (set_backref_str) {
RMATCH(match)->str = rb_str_new4(str);
}
+ else {
+ /* Note that a MatchData object with RMATCH(match)->str == 0 is incomplete!
+ * We need to hide the object from ObjectSpace.each_object.
+ * https://bugs.ruby-lang.org/issues/19159
+ */
+ rb_obj_hide(match);
+ }
RMATCH(match)->regexp = re;
rb_backref_set(match);
@@ -2270,21 +2277,27 @@ match_values_at(int argc, VALUE *argv, VALUE match)
static VALUE
match_to_s(VALUE match)
{
- VALUE str = rb_reg_last_match(match);
+ VALUE str = rb_reg_last_match(match_check(match));
- match_check(match);
if (NIL_P(str)) str = rb_str_new(0,0);
return str;
}
static int
match_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
- int back_num, int *back_refs, OnigRegex regex, void *arg) {
+ int back_num, int *back_refs, OnigRegex regex, void *arg)
+{
struct MEMO *memo = MEMO_CAST(arg);
VALUE hash = memo->v1;
VALUE match = memo->v2;
+ long symbolize = memo->u3.state;
VALUE key = rb_enc_str_new((const char *)name, name_end-name, regex->enc);
+
+ if (symbolize > 0) {
+ key = rb_str_intern(key);
+ }
+
VALUE value;
int i;
@@ -2350,6 +2363,75 @@ match_named_captures(VALUE match)
/*
* call-seq:
+ * deconstruct_keys(array_of_names) -> hash
+ *
+ * Returns a hash of the named captures for the given names.
+ *
+ * m = /(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})/.match("18:37:22")
+ * m.deconstruct_keys([:hours, :minutes]) # => {:hours => "18", :minutes => "37"}
+ * m.deconstruct_keys(nil) # => {:hours => "18", :minutes => "37", :seconds => "22"}
+ *
+ * Returns an empty hash of no named captures were defined:
+ *
+ * m = /(\d{2}):(\d{2}):(\d{2})/.match("18:37:22")
+ * m.deconstruct_keys(nil) # => {}
+ *
+ */
+static VALUE
+match_deconstruct_keys(VALUE match, VALUE keys)
+{
+ VALUE h;
+ long i;
+
+ match_check(match);
+
+ if (NIL_P(RMATCH(match)->regexp)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ if (NIL_P(keys)) {
+ h = rb_hash_new_with_size(onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)));
+
+ struct MEMO *memo;
+ memo = MEMO_NEW(h, match, 1);
+
+ onig_foreach_name(RREGEXP_PTR(RMATCH(match)->regexp), match_named_captures_iter, (void*)memo);
+
+ return h;
+ }
+
+ Check_Type(keys, T_ARRAY);
+
+ if (onig_number_of_names(RREGEXP_PTR(RMATCH(match)->regexp)) < RARRAY_LEN(keys)) {
+ return rb_hash_new_with_size(0);
+ }
+
+ h = rb_hash_new_with_size(RARRAY_LEN(keys));
+
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+ VALUE name;
+
+ Check_Type(key, T_SYMBOL);
+
+ name = rb_sym2str(key);
+
+ int num = NAME_TO_NUMBER(RMATCH_REGS(match), RMATCH(match)->regexp, RMATCH(match)->regexp,
+ RSTRING_PTR(name), RSTRING_END(name));
+
+ if (num >= 0) {
+ rb_hash_aset(h, key, rb_reg_nth_match(num, match));
+ }
+ else {
+ return h;
+ }
+ }
+
+ return h;
+}
+
+/*
+ * call-seq:
* string -> string
*
* Returns the target string if it was frozen;
@@ -2719,14 +2801,18 @@ unescape_unicode_bmp(const char **pp, const char *end,
}
static int
-unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
+unescape_nonascii0(const char **pp, const char *end, rb_encoding *enc,
VALUE buf, rb_encoding **encp, int *has_property,
- onig_errmsg_buffer err, int options)
+ onig_errmsg_buffer err, int options, int recurse)
{
+ const char *p = *pp;
unsigned char c;
char smallbuf[2];
int in_char_class = 0;
+ int parens = 1; /* ignored unless recurse is true */
+ int extended_mode = options & ONIG_OPTION_EXTEND;
+begin_scan:
while (p < end) {
int chlen = rb_enc_precise_mbclen(p, end, enc);
if (!MBCLEN_CHARFOUND_P(chlen)) {
@@ -2785,7 +2871,7 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
case 'C': /* \C-X, \C-\M-X */
case 'M': /* \M-X, \M-\C-X, \M-\cX */
p = p-2;
- if (enc == rb_usascii_encoding()) {
+ if (rb_is_usascii_enc(enc)) {
const char *pbeg = p;
int byte = read_escaped_byte(&p, end, err);
if (byte == -1) return -1;
@@ -2838,9 +2924,13 @@ escape_asis:
break;
case '#':
- if ((options & ONIG_OPTION_EXTEND) && !in_char_class) {
+ if (extended_mode && !in_char_class) {
/* consume and ignore comment in extended regexp */
- while ((p < end) && ((c = *p++) != '\n'));
+ while ((p < end) && ((c = *p++) != '\n')) {
+ if ((c & 0x80) && !*encp && enc == rb_utf8_encoding()) {
+ *encp = enc;
+ }
+ }
break;
}
rb_str_buf_cat(buf, (char *)&c, 1);
@@ -2855,51 +2945,141 @@ escape_asis:
}
rb_str_buf_cat(buf, (char *)&c, 1);
break;
+ case ')':
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ if (!in_char_class && recurse) {
+ if (--parens == 0) {
+ *pp = p;
+ return 0;
+ }
+ }
+ break;
case '(':
- if (!in_char_class && p + 1 < end && *p == '?' && *(p+1) == '#') {
- /* (?# is comment inside any regexp, and content inside should be ignored */
- const char *orig_p = p;
- int cont = 1;
-
- while (cont && (p < end)) {
- switch (c = *p++) {
- default:
- if (!(c & 0x80)) break;
- --p;
- /* fallthrough */
- case '\\':
- chlen = rb_enc_precise_mbclen(p, end, enc);
- if (!MBCLEN_CHARFOUND_P(chlen)) {
- goto invalid_multibyte;
+ if (!in_char_class && p + 1 < end && *p == '?') {
+ if (*(p+1) == '#') {
+ /* (?# is comment inside any regexp, and content inside should be ignored */
+ const char *orig_p = p;
+ int cont = 1;
+
+ while (cont && (p < end)) {
+ switch (c = *p++) {
+ default:
+ if (!(c & 0x80)) break;
+ if (!*encp && enc == rb_utf8_encoding()) {
+ *encp = enc;
+ }
+ --p;
+ /* fallthrough */
+ case '\\':
+ chlen = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(chlen)) {
+ goto invalid_multibyte;
+ }
+ p += MBCLEN_CHARFOUND_LEN(chlen);
+ break;
+ case ')':
+ cont = 0;
+ break;
}
- p += MBCLEN_CHARFOUND_LEN(chlen);
- break;
- case ')':
- cont = 0;
- break;
}
+
+ if (cont) {
+ /* unterminated (?#, rewind so it is syntax error */
+ p = orig_p;
+ c = '(';
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ }
+ break;
}
+ else {
+ /* potential change of extended option */
+ int invert = 0;
+ int local_extend = 0;
+ const char *s;
+
+ if (recurse) {
+ parens++;
+ }
- if (cont) {
- /* unterminated (?#, rewind so it is syntax error */
- p = orig_p;
- c = '(';
- rb_str_buf_cat(buf, (char *)&c, 1);
+ for(s = p+1; s < end; s++) {
+ switch(*s) {
+ case 'x':
+ local_extend = invert ? -1 : 1;
+ break;
+ case '-':
+ invert = 1;
+ break;
+ case ':':
+ case ')':
+ if (local_extend == 0 ||
+ (local_extend == -1 && !extended_mode) ||
+ (local_extend == 1 && extended_mode)) {
+ /* no changes to extended flag */
+ goto fallthrough;
+ }
+
+ if (*s == ':') {
+ /* change extended flag until ')' */
+ int local_options = options;
+ if (local_extend == 1) {
+ local_options |= ONIG_OPTION_EXTEND;
+ }
+ else {
+ local_options &= ~ONIG_OPTION_EXTEND;
+ }
+
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ int ret = unescape_nonascii0(&p, end, enc, buf, encp,
+ has_property, err,
+ local_options, 1);
+ if (ret < 0) return ret;
+ goto begin_scan;
+ }
+ else {
+ /* change extended flag for rest of expression */
+ extended_mode = local_extend == 1;
+ goto fallthrough;
+ }
+ case 'i':
+ case 'm':
+ case 'a':
+ case 'd':
+ case 'u':
+ /* other option flags, ignored during scanning */
+ break;
+ default:
+ /* other character, no extended flag change*/
+ goto fallthrough;
+ }
+ }
}
}
- else {
- rb_str_buf_cat(buf, (char *)&c, 1);
+ else if (!in_char_class && recurse) {
+ parens++;
}
- break;
+ /* FALLTHROUGH */
default:
+fallthrough:
rb_str_buf_cat(buf, (char *)&c, 1);
break;
}
}
+ if (recurse) {
+ *pp = p;
+ }
return 0;
}
+static int
+unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
+ VALUE buf, rb_encoding **encp, int *has_property,
+ onig_errmsg_buffer err, int options)
+{
+ return unescape_nonascii0(&p, end, enc, buf, encp, has_property,
+ err, options, 0);
+}
+
static VALUE
rb_reg_preprocess(const char *p, const char *end, rb_encoding *enc,
rb_encoding **fixed_enc, onig_errmsg_buffer err, int options)
@@ -3658,9 +3838,30 @@ str_to_option(VALUE str)
return flag;
}
+static void
+set_timeout(rb_hrtime_t *hrt, VALUE timeout)
+{
+ double timeout_d = NIL_P(timeout) ? 0.0 : NUM2DBL(timeout);
+ if (!NIL_P(timeout) && timeout_d <= 0) {
+ rb_raise(rb_eArgError, "invalid timeout: %"PRIsVALUE, timeout);
+ }
+ double2hrtime(hrt, timeout_d);
+}
+
+struct reg_init_args {
+ VALUE str;
+ VALUE timeout;
+ rb_encoding *enc;
+ int flags;
+};
+
+static VALUE reg_extract_args(int argc, VALUE *argv, struct reg_init_args *args);
+static VALUE reg_init_args(VALUE self, VALUE str, rb_encoding *enc, int flags);
+void rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *suggest, ...);
+
/*
* call-seq:
- * Regexp.new(string, options = 0, n_flag = nil, timeout: nil) -> regexp
+ * Regexp.new(string, options = 0, timeout: nil) -> regexp
* Regexp.new(regexp, timeout: nil) -> regexp
*
* With argument +string+ given, returns a new regexp with the given string
@@ -3678,27 +3879,23 @@ str_to_option(VALUE str)
* Regexp.new('foo', 'im') # => /foo/im
*
* - The logical OR of one or more of the constants
- * Regexp::EXTENDED, Regexp::IGNORECASE, and Regexp::MULTILINE:
+ * Regexp::EXTENDED, Regexp::IGNORECASE, Regexp::MULTILINE, and
+ * Regexp::NOENCODING:
*
* Regexp.new('foo', Regexp::IGNORECASE) # => /foo/i
* Regexp.new('foo', Regexp::EXTENDED) # => /foo/x
* Regexp.new('foo', Regexp::MULTILINE) # => /foo/m
+ * Regexp.new('foo', Regexp::NOENCODING) # => /foo/n
* flags = Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE
* Regexp.new('foo', flags) # => /foo/mix
*
* - +nil+ or +false+, which is ignored.
*
- * If optional argument +n_flag+ if it is a string starts with
- * <code>'n'</code> or <code>'N'</code>, the encoding of +string+ is
- * ignored and the new regexp encoding is fixed to +ASCII-8BIT+ or
- * +US-ASCII+, by its content.
- *
- * Regexp.new('foo', nil, 'n') # => /foo/n
- * Regexp.new("\u3042", nil, 'n') # => /\xE3\x81\x82/n
- *
* If optional keyword argument +timeout+ is given,
* its float value overrides the timeout interval for the class,
* Regexp.timeout.
+ * If +nil+ is passed as +timeout, it uses the timeout interval
+ * for the class, Regexp.timeout.
*
* With argument +regexp+ given, returns a new regexp. The source,
* options, timeout are the same as +regexp+. +options+ and +n_flag+
@@ -3719,26 +3916,43 @@ str_to_option(VALUE str)
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
+ struct reg_init_args args;
+
+ reg_extract_args(argc, argv, &args);
+ reg_init_args(self, args.str, args.enc, args.flags);
+
+ set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);
+
+ return self;
+}
+
+static VALUE
+reg_extract_args(int argc, VALUE *argv, struct reg_init_args *args)
+{
int flags = 0;
- VALUE str;
rb_encoding *enc = 0;
+ VALUE str, src, opts = Qundef, n_flag = Qundef, kwargs;
+ VALUE re = Qnil;
- VALUE src, opts = Qundef, n_flag = Qundef, kwargs, timeout = Qnil;
-
- rb_scan_args(argc, argv, "12:", &src, &opts, &n_flag, &kwargs);
+ argc = rb_scan_args(argc, argv, "12:", &src, &opts, &n_flag, &kwargs);
+ args->timeout = Qnil;
if (!NIL_P(kwargs)) {
static ID keywords[1];
if (!keywords[0]) {
keywords[0] = rb_intern_const("timeout");
}
- rb_get_kwargs(kwargs, keywords, 0, 1, &timeout);
+ rb_get_kwargs(kwargs, keywords, 0, 1, &args->timeout);
+ }
+
+ if (argc == 3) {
+ rb_warn_deprecated_to_remove("3.3", "3rd argument to Regexp.new", "2nd argument");
}
if (RB_TYPE_P(src, T_REGEXP)) {
- VALUE re = src;
+ re = src;
- if (opts != Qnil) {
+ if (!NIL_P(opts)) {
rb_warn("flags ignored");
}
rb_reg_check(re);
@@ -3746,38 +3960,35 @@ rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
str = RREGEXP_SRC(re);
}
else {
- if (opts != Qundef) {
+ if (!UNDEF_P(opts)) {
int f;
if (FIXNUM_P(opts)) flags = FIX2INT(opts);
else if ((f = str_to_option(opts)) >= 0) flags = f;
else if (!NIL_P(opts) && rb_bool_expected(opts, "ignorecase", FALSE))
flags = ONIG_OPTION_IGNORECASE;
}
- if (n_flag != Qundef && !NIL_P(n_flag)) {
+ if (!NIL_OR_UNDEF_P(n_flag)) {
char *kcode = StringValuePtr(n_flag);
if (kcode[0] == 'n' || kcode[0] == 'N') {
enc = rb_ascii8bit_encoding();
flags |= ARG_ENCODING_NONE;
}
- else {
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "encoding option is ignored - %s", kcode);
- }
}
str = StringValue(src);
}
+ args->str = str;
+ args->enc = enc;
+ args->flags = flags;
+ return re;
+}
+
+static VALUE
+reg_init_args(VALUE self, VALUE str, rb_encoding *enc, int flags)
+{
if (enc && rb_enc_get(str) != enc)
rb_reg_init_str_enc(self, str, enc, flags);
else
rb_reg_init_str(self, str, flags);
-
- regex_t *reg = RREGEXP_PTR(self);
-
- {
- double limit = NIL_P(timeout) ? 0.0 : NUM2DBL(timeout);
- if (limit < 0) limit = 0;
- double2hrtime(&reg->timelimit, limit);
- }
-
return self;
}
@@ -4104,6 +4315,40 @@ rb_reg_s_union_m(VALUE self, VALUE args)
return rb_reg_s_union(self, args);
}
+/*
+ * call-seq:
+ * Regexp.linear_time?(re)
+ * Regexp.linear_time?(string, options = 0)
+ *
+ * Returns +true+ if matching against <tt>re</tt> can be
+ * done in linear time to the input string.
+ *
+ * Regexp.linear_time?(/re/) # => true
+ *
+ * Note that this is a property of the ruby interpreter, not of the argument
+ * regular expression. Identical regexp can or cannot run in linear time
+ * depending on your ruby binary. Neither forward nor backward compatibility
+ * is guaranteed about the return value of this method. Our current algorithm
+ * is (*1) but this is subject to change in the future. Alternative
+ * implementations can also behave differently. They might always return
+ * false for everything.
+ *
+ * (*1): https://doi.org/10.1109/SP40001.2021.00032
+ *
+ */
+static VALUE
+rb_reg_s_linear_time_p(int argc, VALUE *argv, VALUE self)
+{
+ struct reg_init_args args;
+ VALUE re = reg_extract_args(argc, argv, &args);
+
+ if (NIL_P(re)) {
+ re = reg_init_args(rb_reg_alloc(), args.str, args.enc, args.flags);
+ }
+
+ return RBOOL(onig_check_linear_time(RREGEXP_PTR(re)));
+}
+
/* :nodoc: */
static VALUE
rb_reg_init_copy(VALUE copy, VALUE re)
@@ -4399,16 +4644,13 @@ rb_reg_s_timeout_get(VALUE dummy)
*/
static VALUE
-rb_reg_s_timeout_set(VALUE dummy, VALUE limit)
+rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
- double timeout = NIL_P(limit) ? 0.0 : NUM2DBL(limit);
-
rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");
- if (timeout < 0) timeout = 0;
- double2hrtime(&rb_reg_match_time_limit, timeout);
+ set_timeout(&rb_reg_match_time_limit, timeout);
- return limit;
+ return timeout;
}
/*
@@ -4478,12 +4720,13 @@ Init_Regexp(void)
rb_cRegexp = rb_define_class("Regexp", rb_cObject);
rb_define_alloc_func(rb_cRegexp, rb_reg_s_alloc);
- rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance_pass_kw, -1);
rb_define_singleton_method(rb_cRegexp, "quote", rb_reg_s_quote, 1);
rb_define_singleton_method(rb_cRegexp, "escape", rb_reg_s_quote, 1);
rb_define_singleton_method(rb_cRegexp, "union", rb_reg_s_union_m, -2);
rb_define_singleton_method(rb_cRegexp, "last_match", rb_reg_s_last_match, -1);
rb_define_singleton_method(rb_cRegexp, "try_convert", rb_reg_s_try_convert, 1);
+ rb_define_singleton_method(rb_cRegexp, "linear_time?", rb_reg_s_linear_time_p, -1);
rb_define_method(rb_cRegexp, "initialize", rb_reg_initialize_m, -1);
rb_define_method(rb_cRegexp, "initialize_copy", rb_reg_init_copy, 1);
@@ -4542,7 +4785,9 @@ Init_Regexp(void)
rb_define_method(rb_cMatch, "to_a", match_to_a, 0);
rb_define_method(rb_cMatch, "[]", match_aref, -1);
rb_define_method(rb_cMatch, "captures", match_captures, 0);
+ rb_define_alias(rb_cMatch, "deconstruct", "captures");
rb_define_method(rb_cMatch, "named_captures", match_named_captures, 0);
+ rb_define_method(rb_cMatch, "deconstruct_keys", match_deconstruct_keys, 1);
rb_define_method(rb_cMatch, "values_at", match_values_at, -1);
rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0);
rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0);
diff --git a/regcomp.c b/regcomp.c
index 94640639d8..be85d85f93 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -341,7 +341,7 @@ static int
select_str_opcode(int mb_len, OnigDistance byte_len, int ignore_case)
{
int op;
- OnigDistance str_len = (byte_len + mb_len - 1) / mb_len;
+ OnigDistance str_len = roomof(byte_len, mb_len);
if (ignore_case) {
switch (str_len) {
diff --git a/regenc.c b/regenc.c
index 16d62fdf40..eb523e1ae5 100644
--- a/regenc.c
+++ b/regenc.c
@@ -52,6 +52,21 @@ onigenc_set_default_encoding(OnigEncoding enc)
}
extern int
+onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
+{
+ int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
+ if (ONIGENC_MBCLEN_CHARFOUND_P(ret)) {
+ ret = ONIGENC_MBCLEN_CHARFOUND_LEN(ret);
+ if (p + ret > e) ret = (int)(e - p); // just for case
+ return ret;
+ }
+ else if (ONIGENC_MBCLEN_NEEDMORE_P(ret)) {
+ return (int)(e - p);
+ }
+ return p < e ? 1 : 0;
+}
+
+extern int
onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
{
int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
diff --git a/regenc.h b/regenc.h
index 8c4ff0483b..4b4d21a715 100644
--- a/regenc.h
+++ b/regenc.h
@@ -91,7 +91,7 @@ typedef struct {
#define ONIG_CHECK_NULL_RETURN(p) if (ONIG_IS_NULL(p)) return NULL
#define ONIG_CHECK_NULL_RETURN_VAL(p,val) if (ONIG_IS_NULL(p)) return (val)
-#define enclen(enc,p,e) ((enc->max_enc_len == enc->min_enc_len) ? enc->min_enc_len : ONIGENC_MBC_ENC_LEN(enc,p,e))
+#define enclen(enc,p,e) ((enc->max_enc_len == enc->min_enc_len) ? (p < e ? enc->min_enc_len : 0) : ONIGENC_MBC_ENC_LEN(enc,p,e))
/* character types bit flag */
#define BIT_CTYPE_NEWLINE (1<< ONIGENC_CTYPE_NEWLINE)
@@ -118,6 +118,11 @@ typedef struct {
typedef struct {
short int len;
+#if defined(__has_attribute)
+# if __has_attribute(nonstring)
+ __attribute__((nonstring))
+# endif
+#endif
const UChar name[6];
int ctype;
} PosixBracketEntryType;
@@ -125,10 +130,9 @@ typedef struct {
#define POSIX_BRACKET_ENTRY_INIT(name, ctype) \
{(short int )(sizeof(name) - 1), name, (ctype)}
-#ifndef numberof
-# define numberof(array) (int )(sizeof(array) / sizeof((array)[0]))
-#endif
-
+#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
+#define roomof(x, y) (((x) + (y) - 1) / (y))
+#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
#define USE_CRNL_AS_LINE_TERMINATOR
#define USE_UNICODE_PROPERTIES
diff --git a/regexec.c b/regexec.c
index c77d48b1d9..47fc88c9c2 100644
--- a/regexec.c
+++ b/regexec.c
@@ -231,6 +231,488 @@ onig_get_capture_tree(OnigRegion* region)
}
#endif /* USE_CAPTURE_HISTORY */
+#ifdef USE_CACHE_MATCH_OPT
+
+/* count number of jump-like opcodes for allocation of cache memory. */
+static OnigPosition
+count_num_cache_opcode(regex_t* reg, long* num, long* table_size)
+{
+ UChar* p = reg->p;
+ UChar* pend = p + reg->used;
+ LengthType len;
+ MemNumType mem;
+ MemNumType current_mem = -1;
+ long current_mem_num = 0;
+ OnigEncoding enc = reg->enc;
+
+ *num = 0;
+ *table_size = 0;
+
+ while (p < pend) {
+ switch (*p++) {
+ case OP_FINISH:
+ case OP_END:
+ break;
+
+ case OP_EXACT1: p++; break;
+ case OP_EXACT2: p += 2; break;
+ case OP_EXACT3: p += 3; break;
+ case OP_EXACT4: p += 4; break;
+ case OP_EXACT5: p += 5; break;
+ case OP_EXACTN:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_EXACTMB2N1: p += 2; break;
+ case OP_EXACTMB2N2: p += 4; break;
+ case OP_EXACTMB2N3: p += 6; break;
+ case OP_EXACTMB2N:
+ GET_LENGTH_INC(len, p); p += len * 2; break;
+ case OP_EXACTMB3N:
+ GET_LENGTH_INC(len, p); p += len * 3; break;
+ case OP_EXACTMBN:
+ {
+ int mb_len;
+ GET_LENGTH_INC(mb_len, p);
+ GET_LENGTH_INC(len, p);
+ p += mb_len * len;
+ }
+ break;
+
+ case OP_EXACT1_IC:
+ len = enclen(enc, p, pend); p += len; break;
+ case OP_EXACTN_IC:
+ GET_LENGTH_INC(len, p); p += len; break;
+
+ case OP_CCLASS:
+ case OP_CCLASS_NOT:
+ p += SIZE_BITSET; break;
+ case OP_CCLASS_MB:
+ case OP_CCLASS_MB_NOT:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_CCLASS_MIX:
+ case OP_CCLASS_MIX_NOT:
+ p += SIZE_BITSET;
+ GET_LENGTH_INC(len, p);
+ p += len;
+ break;
+
+ case OP_ANYCHAR:
+ case OP_ANYCHAR_ML:
+ break;
+ case OP_ANYCHAR_STAR:
+ case OP_ANYCHAR_ML_STAR:
+ *num += 1; *table_size += 1; break;
+ case OP_ANYCHAR_STAR_PEEK_NEXT:
+ case OP_ANYCHAR_ML_STAR_PEEK_NEXT:
+ p++; *num += 1; *table_size += 1; break;
+
+ case OP_WORD:
+ case OP_NOT_WORD:
+ case OP_WORD_BOUND:
+ case OP_NOT_WORD_BOUND:
+ case OP_WORD_BEGIN:
+ case OP_WORD_END:
+ break;
+
+ case OP_ASCII_WORD:
+ case OP_NOT_ASCII_WORD:
+ case OP_ASCII_WORD_BOUND:
+ case OP_NOT_ASCII_WORD_BOUND:
+ case OP_ASCII_WORD_BEGIN:
+ case OP_ASCII_WORD_END:
+ break;
+
+ case OP_BEGIN_BUF:
+ case OP_END_BUF:
+ case OP_BEGIN_LINE:
+ case OP_END_LINE:
+ case OP_SEMI_END_BUF:
+ case OP_BEGIN_POSITION:
+ break;
+
+ case OP_BACKREF1:
+ case OP_BACKREF2:
+ case OP_BACKREFN:
+ case OP_BACKREFN_IC:
+ case OP_BACKREF_MULTI:
+ case OP_BACKREF_MULTI_IC:
+ case OP_BACKREF_WITH_LEVEL:
+ goto fail;
+
+ case OP_MEMORY_START:
+ case OP_MEMORY_START_PUSH:
+ case OP_MEMORY_END_PUSH:
+ case OP_MEMORY_END_PUSH_REC:
+ case OP_MEMORY_END:
+ case OP_MEMORY_END_REC:
+ p += SIZE_MEMNUM; break;
+
+ case OP_KEEP:
+ break;
+
+ case OP_FAIL:
+ break;
+ case OP_JUMP:
+ p += SIZE_RELADDR;
+ break;
+ case OP_PUSH:
+ p += SIZE_RELADDR;
+ *num += 1;
+ *table_size += 1;
+ break;
+ case OP_POP:
+ break;
+ case OP_PUSH_OR_JUMP_EXACT1:
+ case OP_PUSH_IF_PEEK_NEXT:
+ p += SIZE_RELADDR + 1; *num += 1; *table_size += 1; break;
+ case OP_REPEAT:
+ case OP_REPEAT_NG:
+ if (current_mem != -1) {
+ // A nested OP_REPEAT is not yet supported.
+ goto fail;
+ }
+ GET_MEMNUM_INC(mem, p);
+ p += SIZE_RELADDR;
+ if (reg->repeat_range[mem].lower == 0) {
+ *num += 1;
+ *table_size += 1;
+ }
+ reg->repeat_range[mem].base_num = *num;
+ current_mem = mem;
+ current_mem_num = *num;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, p);
+ if (mem != current_mem) {
+ // A lone or invalid OP_REPEAT_INC is found.
+ goto fail;
+ }
+ {
+ long inner_num = *num - current_mem_num;
+ OnigRepeatRange *repeat_range = &reg->repeat_range[mem];
+ repeat_range->inner_num = inner_num;
+ *num -= inner_num;
+ *num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower);
+ if (repeat_range->lower < repeat_range->upper) {
+ *table_size += 1;
+ }
+ current_mem = -1;
+ current_mem_num = 0;
+ }
+ break;
+ case OP_REPEAT_INC_SG:
+ case OP_REPEAT_INC_NG_SG:
+ // TODO: Support nested OP_REPEAT.
+ goto fail;
+ case OP_NULL_CHECK_START:
+ case OP_NULL_CHECK_END:
+ case OP_NULL_CHECK_END_MEMST:
+ case OP_NULL_CHECK_END_MEMST_PUSH:
+ p += SIZE_MEMNUM; break;
+
+ case OP_PUSH_POS:
+ case OP_POP_POS:
+ case OP_PUSH_POS_NOT:
+ case OP_FAIL_POS:
+ case OP_PUSH_STOP_BT:
+ case OP_POP_STOP_BT:
+ case OP_LOOK_BEHIND:
+ case OP_PUSH_LOOK_BEHIND_NOT:
+ case OP_FAIL_LOOK_BEHIND_NOT:
+ case OP_PUSH_ABSENT_POS:
+ case OP_ABSENT_END:
+ case OP_ABSENT:
+ goto fail;
+
+ case OP_CALL:
+ case OP_RETURN:
+ goto fail;
+
+ case OP_CONDITION:
+ goto fail;
+
+ case OP_STATE_CHECK_PUSH:
+ case OP_STATE_CHECK_PUSH_OR_JUMP:
+ case OP_STATE_CHECK:
+ case OP_STATE_CHECK_ANYCHAR_STAR:
+ case OP_STATE_CHECK_ANYCHAR_ML_STAR:
+ goto fail;
+
+ case OP_SET_OPTION_PUSH:
+ case OP_SET_OPTION:
+ p += SIZE_OPTION;
+ break;
+
+ default:
+ goto bytecode_error;
+ }
+ }
+
+ return 0;
+
+fail:
+ *num = NUM_CACHE_OPCODE_FAIL;
+ return 0;
+
+bytecode_error:
+ return ONIGERR_UNDEFINED_BYTECODE;
+}
+
+static OnigPosition
+init_cache_index_table(regex_t* reg, OnigCacheIndex *table)
+{
+ UChar* pbegin;
+ UChar* p = reg->p;
+ UChar* pend = p + reg->used;
+ LengthType len;
+ MemNumType mem;
+ MemNumType current_mem = -1;
+ long num = 0;
+ long current_mem_num = 0;
+ OnigEncoding enc = reg->enc;
+
+ while (p < pend) {
+ pbegin = p;
+ switch (*p++) {
+ case OP_FINISH:
+ case OP_END:
+ break;
+
+ case OP_EXACT1: p++; break;
+ case OP_EXACT2: p += 2; break;
+ case OP_EXACT3: p += 3; break;
+ case OP_EXACT4: p += 4; break;
+ case OP_EXACT5: p += 5; break;
+ case OP_EXACTN:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_EXACTMB2N1: p += 2; break;
+ case OP_EXACTMB2N2: p += 4; break;
+ case OP_EXACTMB2N3: p += 6; break;
+ case OP_EXACTMB2N:
+ GET_LENGTH_INC(len, p); p += len * 2; break;
+ case OP_EXACTMB3N:
+ GET_LENGTH_INC(len, p); p += len * 3; break;
+ case OP_EXACTMBN:
+ {
+ int mb_len;
+ GET_LENGTH_INC(mb_len, p);
+ GET_LENGTH_INC(len, p);
+ p += mb_len * len;
+ }
+ break;
+
+ case OP_EXACT1_IC:
+ len = enclen(enc, p, pend); p += len; break;
+ case OP_EXACTN_IC:
+ GET_LENGTH_INC(len, p); p += len; break;
+
+ case OP_CCLASS:
+ case OP_CCLASS_NOT:
+ p += SIZE_BITSET; break;
+ case OP_CCLASS_MB:
+ case OP_CCLASS_MB_NOT:
+ GET_LENGTH_INC(len, p); p += len; break;
+ case OP_CCLASS_MIX:
+ case OP_CCLASS_MIX_NOT:
+ p += SIZE_BITSET;
+ GET_LENGTH_INC(len, p);
+ p += len;
+ break;
+
+ case OP_ANYCHAR:
+ case OP_ANYCHAR_ML:
+ break;
+ case OP_ANYCHAR_STAR:
+ case OP_ANYCHAR_ML_STAR:
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_ANYCHAR_STAR_PEEK_NEXT:
+ case OP_ANYCHAR_ML_STAR_PEEK_NEXT:
+ p++;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+
+ case OP_WORD:
+ case OP_NOT_WORD:
+ case OP_WORD_BOUND:
+ case OP_NOT_WORD_BOUND:
+ case OP_WORD_BEGIN:
+ case OP_WORD_END:
+ break;
+
+ case OP_ASCII_WORD:
+ case OP_NOT_ASCII_WORD:
+ case OP_ASCII_WORD_BOUND:
+ case OP_NOT_ASCII_WORD_BOUND:
+ case OP_ASCII_WORD_BEGIN:
+ case OP_ASCII_WORD_END:
+ break;
+
+ case OP_BEGIN_BUF:
+ case OP_END_BUF:
+ case OP_BEGIN_LINE:
+ case OP_END_LINE:
+ case OP_SEMI_END_BUF:
+ case OP_BEGIN_POSITION:
+ break;
+
+ case OP_BACKREF1:
+ case OP_BACKREF2:
+ case OP_BACKREFN:
+ case OP_BACKREFN_IC:
+ case OP_BACKREF_MULTI:
+ case OP_BACKREF_MULTI_IC:
+ case OP_BACKREF_WITH_LEVEL:
+ goto unexpected_bytecode_error;
+
+ case OP_MEMORY_START:
+ case OP_MEMORY_START_PUSH:
+ case OP_MEMORY_END_PUSH:
+ case OP_MEMORY_END_PUSH_REC:
+ case OP_MEMORY_END:
+ case OP_MEMORY_END_REC:
+ p += SIZE_MEMNUM; break;
+
+ case OP_KEEP:
+ break;
+
+ case OP_FAIL:
+ break;
+ case OP_JUMP:
+ p += SIZE_RELADDR;
+ break;
+ case OP_PUSH:
+ p += SIZE_RELADDR;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_POP:
+ break;
+ case OP_PUSH_OR_JUMP_EXACT1:
+ case OP_PUSH_IF_PEEK_NEXT:
+ p += SIZE_RELADDR + 1;
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = current_mem;
+ num++;
+ table++;
+ break;
+ case OP_REPEAT:
+ case OP_REPEAT_NG:
+ GET_MEMNUM_INC(mem, p);
+ p += SIZE_RELADDR;
+ if (reg->repeat_range[mem].lower == 0) {
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = -1;
+ num++;
+ table++;
+ }
+ current_mem = mem;
+ current_mem_num = num;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, p);
+ {
+ long inner_num = num - current_mem_num;
+ OnigRepeatRange *repeat_range = &reg->repeat_range[mem];
+ if (repeat_range->lower < repeat_range->upper) {
+ table->addr = pbegin;
+ table->num = num - current_mem_num;
+ table->outer_repeat = mem;
+ table++;
+ }
+ num -= inner_num;
+ num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower);
+ current_mem = -1;
+ current_mem_num = 0;
+ }
+ break;
+ case OP_REPEAT_INC_SG:
+ case OP_REPEAT_INC_NG_SG:
+ // TODO: support OP_REPEAT opcodes.
+ goto unexpected_bytecode_error;
+ case OP_NULL_CHECK_START:
+ case OP_NULL_CHECK_END:
+ case OP_NULL_CHECK_END_MEMST:
+ case OP_NULL_CHECK_END_MEMST_PUSH:
+ p += SIZE_MEMNUM; break;
+
+ case OP_PUSH_POS:
+ case OP_POP_POS:
+ case OP_PUSH_POS_NOT:
+ case OP_FAIL_POS:
+ case OP_PUSH_STOP_BT:
+ case OP_POP_STOP_BT:
+ case OP_LOOK_BEHIND:
+ case OP_PUSH_LOOK_BEHIND_NOT:
+ case OP_FAIL_LOOK_BEHIND_NOT:
+ case OP_PUSH_ABSENT_POS:
+ case OP_ABSENT_END:
+ case OP_ABSENT:
+ goto unexpected_bytecode_error;
+
+ case OP_CALL:
+ case OP_RETURN:
+ goto unexpected_bytecode_error;
+
+ case OP_CONDITION:
+ goto unexpected_bytecode_error;
+
+ case OP_STATE_CHECK_PUSH:
+ case OP_STATE_CHECK_PUSH_OR_JUMP:
+ case OP_STATE_CHECK:
+ case OP_STATE_CHECK_ANYCHAR_STAR:
+ case OP_STATE_CHECK_ANYCHAR_ML_STAR:
+ goto unexpected_bytecode_error;
+
+ case OP_SET_OPTION_PUSH:
+ case OP_SET_OPTION:
+ p += SIZE_OPTION;
+ break;
+
+ default:
+ goto bytecode_error;
+ }
+ }
+
+ return 0;
+
+unexpected_bytecode_error:
+ return ONIGERR_UNEXPECTED_BYTECODE;
+
+bytecode_error:
+ return ONIGERR_UNDEFINED_BYTECODE;
+}
+#else /* USE_MATCH_CACHE */
+static OnigPosition
+count_num_cache_opcode(regex_t* reg, long* num, long* table_size)
+{
+ *num = NUM_CACHE_OPCODE_FAIL;
+ return 0;
+}
+#endif
+
+extern int
+onig_check_linear_time(OnigRegexType* reg)
+{
+ long num = 0, table_size = 0;
+ count_num_cache_opcode(reg, &num, &table_size);
+ return num != NUM_CACHE_OPCODE_FAIL;
+}
+
extern void
onig_region_clear(OnigRegion* region)
{
@@ -413,6 +895,24 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
#define STK_MASK_TO_VOID_TARGET 0x10ff
#define STK_MASK_MEM_END_OR_MARK 0x8000 /* MEM_END or MEM_END_MARK */
+#ifdef USE_CACHE_MATCH_OPT
+#define MATCH_ARG_INIT_CACHE_MATCH_OPT(msa) do {\
+ (msa).enable_cache_match_opt = 0;\
+ (msa).num_fail = 0;\
+ (msa).num_cache_opcode = NUM_CACHE_OPCODE_UNINIT;\
+ (msa).num_cache_table = 0;\
+ (msa).cache_index_table = (OnigCacheIndex *)0;\
+ (msa).match_cache = (uint8_t *)0;\
+} while(0)
+#define MATCH_ARG_FREE_CACHE_MATCH_OPT(msa) do {\
+ if ((msa).cache_index_table) xfree((msa).cache_index_table);\
+ if ((msa).match_cache) xfree((msa).match_cache);\
+} while(0)
+#else
+#define MATCH_ARG_INIT_CACHE_MATCH_OPT(msa)
+#define MATCH_ARG_FREE_CACHE_MATCH_OPT(msa)
+#endif
+
#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
(msa).stack_p = (void* )0;\
@@ -423,6 +923,7 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
(msa).best_len = ONIG_MISMATCH;\
(msa).counter = 0;\
(msa).end_time = 0;\
+ MATCH_ARG_INIT_CACHE_MATCH_OPT(msa);\
} while(0)
#else
# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
@@ -433,6 +934,7 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
(msa).gpos = (arg_gpos);\
(msa).counter = 0;\
(msa).end_time = 0;\
+ MATCH_ARG_INIT_CACHE_MATCH_OPT(msa);\
} while(0)
#endif
@@ -471,9 +973,13 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
if ((msa).state_check_buff_size >= STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE) { \
if ((msa).state_check_buff) xfree((msa).state_check_buff);\
}\
+ MATCH_ARG_FREE_CACHE_MATCH_OPT(msa);\
} while(0)
#else /* USE_COMBINATION_EXPLOSION_CHECK */
-# define MATCH_ARG_FREE(msa) if ((msa).stack_p) xfree((msa).stack_p)
+# define MATCH_ARG_FREE(msa) do {\
+ if ((msa).stack_p) xfree((msa).stack_p);\
+ MATCH_ARG_FREE_CACHE_MATCH_OPT(msa);\
+} while (0)
#endif /* USE_COMBINATION_EXPLOSION_CHECK */
@@ -595,6 +1101,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_TYPE(stack_type) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
STACK_INC;\
} while(0)
@@ -664,6 +1171,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
# define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.state.pcode = (pat);\
stk->u.state.pstr = (s);\
stk->u.state.pstr_prev = (sprev);\
@@ -673,6 +1181,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
# define STACK_PUSH_ENSURED(stack_type,pat) do {\
stk->type = (stack_type);\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.state.pcode = (pat);\
STACK_INC;\
} while(0)
@@ -686,9 +1195,133 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_LOOK_BEHIND_NOT(pat,s,sprev,keep) \
STACK_PUSH(STK_LOOK_BEHIND_NOT,pat,s,sprev,keep)
+#ifdef USE_CACHE_MATCH_OPT
+
+#define DO_CACHE_MATCH_OPT(reg,stk,repeat_stk,enable,p,num_cache_table,num_cache_size,table,pos,match_cache) do {\
+ if (enable) {\
+ long cache_index = find_cache_index_table((reg), (stk), (repeat_stk), (table), (num_cache_table), (p));\
+ if (cache_index >= 0) {\
+ long key = (num_cache_size) * (long)(pos) + cache_index;\
+ long index = key >> 3;\
+ long mask = 1 << (key & 7);\
+ if ((match_cache)[index] & mask) {\
+ goto fail;\
+ }\
+ (match_cache)[index] |= mask;\
+ }\
+ }\
+} while (0)
+
+static long
+find_cache_index_table(regex_t* reg, OnigStackType *stk, OnigStackIndex *repeat_stk, OnigCacheIndex* table, long num_cache_table, UChar* p)
+{
+ long l = 0, r = num_cache_table - 1, m = 0;
+ OnigCacheIndex* item;
+ OnigRepeatRange* range;
+ OnigStackType *stkp;
+ int count = 0;
+ int is_inc = *p == OP_REPEAT_INC || *p == OP_REPEAT_INC_NG;
+
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (table[m].addr == p) break;
+ if (table[m].addr < p) l = m + 1;
+ else r = m - 1;
+ }
+
+ if (!(0 <= m && m < num_cache_table && table[m].addr == p)) {
+ return -1;
+ }
+
+ item = &table[m];
+ if (item->outer_repeat == -1) {
+ return item->num;
+ }
+
+ range = &reg->repeat_range[item->outer_repeat];
+
+ stkp = &stk[repeat_stk[item->outer_repeat]];
+ count = is_inc ? stkp->u.repeat.count - 1 : stkp->u.repeat.count;
+
+ if (count < range->lower) {
+ return range->base_num + range->inner_num * count + item->num;
+ }
+
+ if (range->upper == 0x7fffffff) {
+ return range->base_num + range->inner_num * (range->lower - (is_inc ? 1 : 0)) + (is_inc ? 0 : 1) + item->num;
+ }
+
+ return range->base_num + range->inner_num * (range->lower - 1) + (range->inner_num + 1) * (count - range->lower + 1) + item->num;
+}
+
+static void
+reset_match_cache(regex_t* reg, UChar* pbegin, UChar* pend, long pos, uint8_t* match_cache, OnigCacheIndex *table, long num_cache_size, long num_cache_table)
+{
+ long l = 0, r = num_cache_table - 1, m1 = 0, m2 = 0;
+ int is_inc = *pend == OP_REPEAT_INC || *pend == OP_REPEAT_INC_NG;
+ OnigCacheIndex *item1, *item2;
+ long k1, k2, base;
+
+ while (l <= r) {
+ m1 = (l + r) / 2;
+ if (table[m1].addr == pbegin) break;
+ if (table[m1].addr < pbegin) l = m1 + 1;
+ else r = m1 - 1;
+ }
+
+ l = 0, r = num_cache_table - 1;
+ while (l <= r) {
+ m2 = (l + r) / 2;
+ if (table[m2].addr == pend) break;
+ if (table[m2].addr < pend) l = m2 + 1;
+ else r = m2 - 1;
+ }
+
+ if (table[m1].addr < pbegin && m1 + 1 < num_cache_table) m1++;
+ if (table[m2].addr > pend && m2 - 1 > 0) m2--;
+
+ item1 = &table[m1];
+ item2 = &table[m2];
+
+ if (item1->outer_repeat < 0) k1 = item1->num;
+ else k1 = reg->repeat_range[item1->outer_repeat].base_num + item1->num;
+
+ if (item2->outer_repeat < 0) k2 = item2->num;
+ else {
+ OnigRepeatRange *range = &reg->repeat_range[item2->outer_repeat];
+ if (range->upper == 0x7fffffff) k2 = range->base_num + range->inner_num * range->lower + (is_inc ? 0 : 1) + item2->num;
+ else k2 = range->base_num + range->inner_num * range->lower + (range->inner_num + 1) * (range->upper - range->lower - (is_inc ? 1 : 0)) + item2->num;
+ }
+
+ base = pos * num_cache_size;
+ k1 += base;
+ k2 += base;
+
+ if ((k1 >> 3) == (k2 >> 3)) {
+ match_cache[k1 >> 3] &= (((1 << (8 - (k2 & 7) - 1)) - 1) << ((k2 & 7) + 1)) | ((1 << (k1 & 7)) - 1);
+ } else {
+ long i = k1 >> 3;
+ if (k1 & 7) {
+ match_cache[k1 >> 3] &= (1 << ((k1 & 7) - 1)) - 1;
+ i++;
+ }
+ if (i < (k2 >> 3)) {
+ xmemset(&match_cache[i], 0, (k2 >> 3) - i);
+ if (k2 & 7) {
+ match_cache[k2 >> 3] &= (((1 << (8 - (k2 & 7) - 1)) - 1) << ((k2 & 7) + 1));
+ }
+ }
+ }
+}
+
+#else
+#define DO_CACHE_MATCH_OPT(reg,stk,repeat_stk,enable,p,num_cache_table,num_cache_size,table,pos,match_cache)
+#endif /* USE_CACHE_MATCH_OPT */
+
#define STACK_PUSH_REPEAT(id, pat) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.repeat.num = (id);\
stk->u.repeat.pcode = (pat);\
stk->u.repeat.count = 0;\
@@ -698,6 +1331,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_REPEAT_INC(sindex) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT_INC;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.repeat_inc.si = (sindex);\
STACK_INC;\
} while(0)
@@ -705,6 +1339,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_START(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_START;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
@@ -717,6 +1352,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_END(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
@@ -728,6 +1364,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_MEM_END_MARK(mnum) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END_MARK;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.mem.num = (mnum);\
STACK_INC;\
} while(0)
@@ -769,6 +1406,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_NULL_CHECK_START(cnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_START;\
+ stk->null_check = (OnigStackIndex)(stk - stk_base);\
stk->u.null_check.num = (cnum);\
stk->u.null_check.pstr = (s);\
STACK_INC;\
@@ -777,6 +1415,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_NULL_CHECK_END(cnum) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_END;\
+ stk->null_check = (OnigStackIndex)(stk - stk_base);\
stk->u.null_check.num = (cnum);\
STACK_INC;\
} while(0)
@@ -784,6 +1423,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_CALL_FRAME(pat) do {\
STACK_ENSURE(1);\
stk->type = STK_CALL_FRAME;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.call_frame.ret_addr = (pat);\
STACK_INC;\
} while(0)
@@ -791,12 +1431,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_RETURN do {\
STACK_ENSURE(1);\
stk->type = STK_RETURN;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
STACK_INC;\
} while(0)
#define STACK_PUSH_ABSENT_POS(start, end) do {\
STACK_ENSURE(1);\
stk->type = STK_ABSENT_POS;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
stk->u.absent_pos.abs_pstr = (start);\
stk->u.absent_pos.end_pstr = (end);\
STACK_INC;\
@@ -960,7 +1602,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
} while(0)
#define STACK_NULL_CHECK(isnull,id,s) do {\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK"); \
@@ -975,7 +1617,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_NULL_CHECK_REC(isnull,id,s) do {\
int level = 0;\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_REC"); \
@@ -994,8 +1636,8 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
}\
} while(0)
-#define STACK_NULL_CHECK_MEMST(isnull,id,s,reg) do {\
- OnigStackType* k = stk;\
+#define STACK_NULL_CHECK_MEMST(isnull,ischange,id,s,reg) do {\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_MEMST"); \
@@ -1011,14 +1653,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
while (k < stk) {\
if (k->type == STK_MEM_START) {\
if (k->u.mem.end == INVALID_STACK_INDEX) {\
- (isnull) = 0; break;\
+ (isnull) = 0; (ischange) = 1; break;\
}\
if (BIT_STATUS_AT(reg->bt_mem_end, k->u.mem.num))\
endp = STACK_AT(k->u.mem.end)->u.mem.pstr;\
else\
endp = (UChar* )k->u.mem.end;\
if (STACK_AT(k->u.mem.start)->u.mem.pstr != endp) {\
- (isnull) = 0; break;\
+ (isnull) = 0; (ischange) = 1; break;\
}\
else if (endp != s) {\
(isnull) = -1; /* empty, but position changed */ \
@@ -1035,7 +1677,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_NULL_CHECK_MEMST_REC(isnull,id,s,reg) do {\
int level = 0;\
- OnigStackType* k = stk;\
+ OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
STACK_BASE_CHECK(k, "STACK_NULL_CHECK_MEMST_REC"); \
@@ -1190,6 +1832,19 @@ static int string_cmp_ic(OnigEncoding enc, int case_fold_flag,
# define ABSENT_END_POS end
#endif /* USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
+int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+
+static inline int
+enclen_approx(OnigEncoding enc, const OnigUChar* p, const OnigUChar* e)
+{
+ if (enc->max_enc_len == enc->min_enc_len) {
+ return (p < e ? enc->min_enc_len : 0);
+ }
+ else {
+ return onigenc_mbclen_approximate(p, e, enc);
+ }
+}
+
#ifdef USE_CAPTURE_HISTORY
static int
@@ -1237,7 +1892,8 @@ make_capture_history_tree(OnigCaptureTreeNode* node, OnigStackType** kp,
#endif /* USE_CAPTURE_HISTORY */
#ifdef USE_BACKREF_WITH_LEVEL
-static int mem_is_in_memp(int mem, int num, UChar* memp)
+static int
+mem_is_in_memp(int mem, int num, UChar* memp)
{
int i;
MemNumType m;
@@ -1448,6 +2104,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
OnigCaseFoldType case_fold_flag = reg->case_fold_flag;
UChar *s, *q, *sbegin;
UChar *p = reg->p;
+ UChar *pbegin = p;
UChar *pkeep;
char *alloca_base;
char *xmalloc_base = NULL;
@@ -1469,7 +2126,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
# define CASE(x) L_##x: sbegin = s; OPCODE_EXEC_HOOK;
# define DEFAULT L_DEFAULT:
# define NEXT sprev = sbegin; JUMP
-# define JUMP RB_GNUC_EXTENSION_BLOCK(goto *oplabels[*p++])
+# define JUMP pbegin = p; RB_GNUC_EXTENSION_BLOCK(goto *oplabels[*p++])
RB_GNUC_EXTENSION static const void *oplabels[] = {
&&L_OP_FINISH, /* matching process terminator (no more alternative) */
@@ -1645,6 +2302,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
# define VM_LOOP \
while (1) { \
OPCODE_EXEC_HOOK; \
+ pbegin = p; \
sbegin = s; \
switch (*p++) {
# define VM_LOOP_END } sprev = sbegin; }
@@ -2037,7 +2695,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int mb_len;
DATA_ENSURE(1);
- mb_len = enclen(encode, s, end);
+ mb_len = enclen_approx(encode, s, end);
DATA_ENSURE(mb_len);
ss = s;
s += mb_len;
@@ -2142,7 +2800,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR) MOP_IN(OP_ANYCHAR);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
s += n;
@@ -2151,7 +2809,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML) MOP_IN(OP_ANYCHAR_ML);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
s += n;
MOP_OUT;
@@ -2159,8 +2817,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_STAR) MOP_IN(OP_ANYCHAR_STAR);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2171,8 +2830,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML_STAR) MOP_IN(OP_ANYCHAR_ML_STAR);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2188,10 +2848,17 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_STAR_PEEK_NEXT) MOP_IN(OP_ANYCHAR_STAR_PEEK_NEXT);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, end - s, msa->match_cache);
if (*p == *s) {
STACK_PUSH_ALT(p + 1, s, sprev, pkeep);
+ } else {
+ /* We need to increment num_fail here, for invoking a cache optimization correctly. */
+ /* Actually, the matching will be failed if we use `OP_ANYCHAR_STAR` simply in this case.*/
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2203,10 +2870,17 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML_STAR_PEEK_NEXT)MOP_IN(OP_ANYCHAR_ML_STAR_PEEK_NEXT);
while (DATA_ENSURE_CHECK1) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
if (*p == *s) {
STACK_PUSH_ALT(p + 1, s, sprev, pkeep);
+ } else {
+ /* We need to increment num_fail here, for invoking a cache optimization correctly. */
+ /* Actually, the matching will be failed if we use `OP_ANYCHAR_STAR_ML` simply in this case.*/
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2229,7 +2903,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2247,7 +2921,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2534,8 +3208,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_MEMORY_END_PUSH_REC) MOP_IN(OP_MEMORY_END_PUSH_REC);
GET_MEMNUM_INC(mem, p);
STACK_GET_MEM_START(mem, stkp); /* should be before push mem-end. */
- STACK_PUSH_MEM_END(mem, s);
mem_start_stk[mem] = GET_STACK_INDEX(stkp);
+ STACK_PUSH_MEM_END(mem, s);
MOP_OUT;
JUMP;
@@ -2589,7 +3263,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
DATA_ENSURE(n);
sprev = s;
STRING_CMP(pstart, s, n);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -2619,8 +3293,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
n = pend - pstart;
DATA_ENSURE(n);
sprev = s;
- STRING_CMP_IC(case_fold_flag, pstart, &s, (int)n, end);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ STRING_CMP_IC(case_fold_flag, pstart, &s, n, end);
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -2655,7 +3329,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STRING_CMP_VALUE(pstart, swork, n, is_fail);
if (is_fail) continue;
s = swork;
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
p += (SIZE_MEMNUM * (tlen - i - 1));
@@ -2790,9 +3464,10 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_NULL_CHECK_END_MEMST) MOP_IN(OP_NULL_CHECK_END_MEMST);
{
int isnull;
+ int ischanged = 0; // set 1 when a loop is empty but memory status is changed.
GET_MEMNUM_INC(mem, p); /* mem: null check id */
- STACK_NULL_CHECK_MEMST(isnull, mem, s, reg);
+ STACK_NULL_CHECK_MEMST(isnull, ischanged, mem, s, reg);
if (isnull) {
# ifdef ONIG_DEBUG_MATCH
fprintf(stderr, "NULL_CHECK_END_MEMST: skip id:%d, s:%"PRIuPTR" (%p)\n",
@@ -2801,6 +3476,29 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (isnull == -1) goto fail;
goto null_check_found;
}
+# ifdef USE_CACHE_MATCH_OPT
+ if (ischanged && msa->enable_cache_match_opt) {
+ RelAddrType rel;
+ OnigUChar *addr;
+ MemNumType mem;
+ UChar* tmp = p;
+ switch (*tmp++) {
+ case OP_JUMP:
+ case OP_PUSH:
+ GET_RELADDR_INC(rel, tmp);
+ addr = tmp + rel;
+ break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ GET_MEMNUM_INC(mem, tmp);
+ addr = STACK_AT(repeat_stk[mem])->u.repeat.pcode;
+ break;
+ default:
+ goto unexpected_bytecode_error;
+ }
+ reset_match_cache(reg, addr, pbegin, (long)(s - str), msa->match_cache, msa->cache_index_table, msa->num_cache_opcode, msa->num_cache_table);
+ }
+# endif
}
MOP_OUT;
JUMP;
@@ -2843,6 +3541,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_PUSH) MOP_IN(OP_PUSH);
GET_RELADDR_INC(addr, p);
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2883,6 +3582,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_POP) MOP_IN(OP_POP);
STACK_POP_ONE;
+ /* We need to increment num_fail here, for invoking a cache optimization correctly, */
+ /* because Onigmo makes a loop, which is pairwise disjoint to the following set, as atomic. */
+#ifdef USE_CACHE_MATCH_OPT
+ msa->num_fail++;
+#endif
MOP_OUT;
JUMP;
@@ -2891,6 +3595,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_RELADDR_INC(addr, p);
if (*p == *s && DATA_ENSURE_CHECK1) {
p++;
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2904,6 +3609,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_RELADDR_INC(addr, p);
if (*p == *s) {
p++;
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
@@ -2922,6 +3628,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_REPEAT(mem, p);
if (reg->repeat_range[mem].lower == 0) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, end - s, msa->match_cache);
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
}
}
@@ -2938,6 +3645,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_REPEAT(mem, p);
if (reg->repeat_range[mem].lower == 0) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
STACK_PUSH_ALT(p, s, sprev, pkeep);
p += addr;
}
@@ -2956,6 +3664,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
/* end of repeat. Nothing to do. */
}
else if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
+ if (*pbegin == OP_REPEAT_INC) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
+ }
STACK_PUSH_ALT(p, s, sprev, pkeep);
p = STACK_AT(si)->u.repeat.pcode; /* Don't use stkp after PUSH. */
}
@@ -2986,6 +3697,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
UChar* pcode = stkp->u.repeat.pcode;
STACK_PUSH_REPEAT_INC(si);
+ if (*pbegin == OP_REPEAT_INC_NG) {
+ DO_CACHE_MATCH_OPT(reg, stk_base, repeat_stk, msa->enable_cache_match_opt, pbegin, msa->num_cache_table, msa->num_cache_opcode, msa->cache_index_table, s - str, msa->match_cache);
+ }
STACK_PUSH_ALT(pcode, s, sprev, pkeep);
}
else {
@@ -3106,6 +3820,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
DATA_ENSURE(0);
p += addr;
}
+ else if (s == end) {
+ /* At the end of the string, just match with it */
+ DATA_ENSURE(0);
+ p += addr;
+ }
else {
STACK_PUSH_ALT(p + addr, s, sprev, pkeep); /* Push possible point. */
n = enclen(encode, s, end);
@@ -3173,6 +3892,49 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
sprev = stk->u.state.pstr_prev;
pkeep = stk->u.state.pkeep;
+#ifdef USE_CACHE_MATCH_OPT
+ if (++msa->num_fail >= (long)(end - str) + 1 && msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) {
+ msa->enable_cache_match_opt = 1;
+ if (msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) {
+ OnigPosition r = count_num_cache_opcode(reg, &msa->num_cache_opcode, &msa->num_cache_table);
+ if (r < 0) goto bytecode_error;
+ }
+ if (msa->num_cache_opcode == NUM_CACHE_OPCODE_FAIL || msa->num_cache_opcode == 0) {
+ msa->enable_cache_match_opt = 0;
+ goto fail_match_cache_opt;
+ }
+ if (msa->cache_index_table == NULL) {
+ OnigCacheIndex *table = (OnigCacheIndex *)xmalloc(msa->num_cache_table * sizeof(OnigCacheIndex));
+ if (table == NULL) {
+ return ONIGERR_MEMORY;
+ }
+ OnigPosition r = init_cache_index_table(reg, table);
+ if (r < 0) {
+ if (r == ONIGERR_UNEXPECTED_BYTECODE) goto unexpected_bytecode_error;
+ else goto bytecode_error;
+ }
+ msa->cache_index_table = table;
+ }
+ size_t len = (end - str) + 1;
+ size_t match_cache_size8 = (size_t)msa->num_cache_opcode * len;
+ /* overflow check */
+ if (match_cache_size8 / len != (size_t)msa->num_cache_opcode) {
+ return ONIGERR_MEMORY;
+ }
+ /* Currently, int is used for the key of match_cache */
+ if (match_cache_size8 >= LONG_MAX_LIMIT) {
+ return ONIGERR_MEMORY;
+ }
+ size_t match_cache_size = (match_cache_size8 >> 3) + (match_cache_size8 & 7 ? 1 : 0);
+ msa->match_cache = (uint8_t*)xmalloc(match_cache_size * sizeof(uint8_t));
+ if (msa->match_cache == NULL) {
+ return ONIGERR_MEMORY;
+ }
+ xmemset(msa->match_cache, 0, match_cache_size * sizeof(uint8_t));
+ }
+ fail_match_cache_opt:
+#endif
+
#ifdef USE_COMBINATION_EXPLOSION_CHECK
if (stk->u.state.state_check != 0) {
stk->type = STK_STATE_CHECK_MARK;
@@ -3909,12 +4671,17 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
UChar* range, UChar** low, UChar** high, UChar** low_prev)
{
UChar *p, *pprev = (UChar* )NULL;
+ size_t input_len = end - str;
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n",
(uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range);
#endif
+ if (reg->dmin > input_len) {
+ return 0;
+ }
+
p = s;
if (reg->dmin > 0) {
if (ONIGENC_IS_SINGLEBYTE(reg->enc)) {
@@ -4051,6 +4818,11 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
UChar** low, UChar** high)
{
UChar *p;
+ size_t input_len = end - str;
+
+ if (reg->dmin > input_len) {
+ return 0;
+ }
range += reg->dmin;
p = s;
diff --git a/regint.h b/regint.h
index 6c88f278c1..cee766ad6e 100644
--- a/regint.h
+++ b/regint.h
@@ -41,6 +41,14 @@
/* for byte-code statistical data. */
/* #define ONIG_DEBUG_STATISTICS */
+/* enable matching optimization by using cache. */
+#define USE_CACHE_MATCH_OPT
+
+#ifdef USE_CACHE_MATCH_OPT
+# define NUM_CACHE_OPCODE_FAIL -1
+# define NUM_CACHE_OPCODE_UNINIT -2
+#endif
+
#if defined(ONIG_DEBUG_PARSE_TREE) || defined(ONIG_DEBUG_MATCH) || \
defined(ONIG_DEBUG_SEARCH) || defined(ONIG_DEBUG_COMPILE) || \
defined(ONIG_DEBUG_STATISTICS) || defined(ONIG_DEBUG_MEMLEAK)
@@ -49,10 +57,11 @@
# endif
#endif
+/* __POWERPC__ added to accommodate Darwin case. */
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
- defined(__powerpc64__) || defined(__aarch64__) || \
+ defined(__powerpc64__) || defined(__POWERPC__) || defined(__aarch64__) || \
defined(__mc68020__)
# define UNALIGNED_WORD_ACCESS 1
# else
@@ -310,9 +319,13 @@ RUBY_SYMBOL_EXPORT_BEGIN
#define ONIG_LAST_CODE_POINT (~((OnigCodePoint )0))
+#define PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type) \
+ ((void)sizeof(char[2 * (sizeof(val) == sizeof(type)) - 1]))
+
#ifdef PLATFORM_UNALIGNED_WORD_ACCESS
# define PLATFORM_GET_INC(val,p,type) do{\
+ PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type);\
val = *(type* )p;\
(p) += sizeof(type);\
} while(0)
@@ -320,7 +333,10 @@ RUBY_SYMBOL_EXPORT_BEGIN
#else
# define PLATFORM_GET_INC(val,p,type) do{\
- xmemcpy(&val, (p), sizeof(type));\
+ PLATFORM_GET_INC_ARGUMENTS_ASSERT(val, type);\
+ type platform_get_value;\
+ xmemcpy(&platform_get_value, (p), sizeof(type));\
+ val = platform_get_value;\
(p) += sizeof(type);\
} while(0)
@@ -378,6 +394,7 @@ typedef unsigned int BitStatusType;
#define INT_MAX_LIMIT ((1UL << (SIZEOF_INT * 8 - 1)) - 1)
+#define LONG_MAX_LIMIT ((1UL << (SIZEOF_LONG * 8 - 1)) - 1)
#define DIGITVAL(code) ((code) - '0')
#define ODIGITVAL(code) DIGITVAL(code)
@@ -819,6 +836,7 @@ typedef intptr_t OnigStackIndex;
typedef struct _OnigStackType {
unsigned int type;
+ OnigStackIndex null_check;
union {
struct {
UChar *pcode; /* byte code position */
@@ -862,6 +880,14 @@ typedef struct _OnigStackType {
} u;
} OnigStackType;
+#ifdef USE_CACHE_MATCH_OPT
+typedef struct {
+ UChar *addr;
+ long num;
+ int outer_repeat;
+} OnigCacheIndex;
+#endif
+
typedef struct {
void* stack_p;
size_t stack_n;
@@ -884,6 +910,14 @@ typedef struct {
#else
uint64_t end_time;
#endif
+#ifdef USE_CACHE_MATCH_OPT
+ long num_fail;
+ int enable_cache_match_opt;
+ long num_cache_opcode;
+ long num_cache_table;
+ OnigCacheIndex* cache_index_table;
+ uint8_t* match_cache;
+#endif
} OnigMatchArg;
diff --git a/regparse.c b/regparse.c
index 4ebd5f1c46..33df0e06c7 100644
--- a/regparse.c
+++ b/regparse.c
@@ -5977,7 +5977,8 @@ node_extended_grapheme_cluster(Node** np, ScanEnv* env)
R_ERR(add_code_range(&(cc->mbuf), env, 0x000A, 0x000A)); /* CR */
R_ERR(add_code_range(&(cc->mbuf), env, 0x000D, 0x000D)); /* LF */
R_ERR(not_code_range_buf(env->enc, cc->mbuf, &inverted_buf, env));
- cc->mbuf = inverted_buf; /* TODO: check what to do with buffer before inversion */
+ bbuf_free(cc->mbuf);
+ cc->mbuf = inverted_buf;
env->warnings_flag &= dup_not_warned; /* TODO: fix false warning */
}
diff --git a/ruby.c b/ruby.c
index 26d263a1b1..09c81db86c 100644
--- a/ruby.c
+++ b/ruby.c
@@ -44,6 +44,7 @@
#include "eval_intern.h"
#include "internal.h"
#include "internal/cmdlineopt.h"
+#include "internal/cont.h"
#include "internal/error.h"
#include "internal/file.h"
#include "internal/inits.h"
@@ -61,6 +62,10 @@
#include "ruby/version.h"
#include "ruby/internal/error.h"
+#define singlebit_only_p(x) !((x) & ((x)-1))
+STATIC_ASSERT(Qnil_1bit_from_Qfalse, singlebit_only_p(Qnil^Qfalse));
+STATIC_ASSERT(Qundef_1bit_from_Qnil, singlebit_only_p(Qundef^Qnil));
+
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
@@ -68,7 +73,7 @@
# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
-void Init_ruby_description(void);
+void Init_ruby_description(ruby_cmdline_options_t *opt);
#ifndef HAVE_STDLIB_H
char *getenv();
@@ -154,7 +159,11 @@ enum feature_flag_bits {
/* END OF DUMPS */
enum dump_flag_bits {
dump_version_v,
+ dump_error_tolerant,
EACH_DUMPS(DEFINE_DUMP, COMMA),
+ dump_error_tolerant_bits = (DUMP_BIT(yydebug) |
+ DUMP_BIT(parsetree) |
+ DUMP_BIT(parsetree_with_comment)),
dump_exit_bits = (DUMP_BIT(yydebug) | DUMP_BIT(syntax) |
DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))
@@ -253,7 +262,7 @@ usage(const char *name, int help, int highlight, int columns)
#if USE_YJIT
# define PLATFORM_JIT_OPTION "--yjit"
#else
-# define PLATFORM_JIT_OPTION "--mjit"
+# define PLATFORM_JIT_OPTION "--mjit (experimental)"
#endif
static const struct ruby_opt_message usage_msg[] = {
M("-0[octal]", "", "specify record separator (\\0, if no argument)"),
@@ -276,12 +285,12 @@ usage(const char *name, int help, int highlight, int columns)
M("-w", "", "turn warnings on for your script"),
M("-W[level=2|:category]", "", "set warning level; 0=silence, 1=medium, 2=verbose"),
M("-x[directory]", "", "strip off text before #!ruby line and perhaps cd to directory"),
- M("--jit", "", "enable JIT for the platform, same as " PLATFORM_JIT_OPTION " (experimental)"),
+ M("--jit", "", "enable JIT for the platform, same as " PLATFORM_JIT_OPTION),
#if USE_MJIT
M("--mjit", "", "enable C compiler-based JIT compiler (experimental)"),
#endif
#if USE_YJIT
- M("--yjit", "", "enable in-process JIT compiler (experimental)"),
+ M("--yjit", "", "enable in-process JIT compiler"),
#endif
M("-h", "", "show this message, --help for more info"),
};
@@ -301,9 +310,9 @@ usage(const char *name, int help, int highlight, int columns)
static const struct ruby_opt_message dumps[] = {
M("insns", "", "instruction sequences"),
M("insns_without_opt", "", "instruction sequences compiled with no optimization"),
- M("yydebug", "", "yydebug of yacc parser generator"),
- M("parsetree", "", "AST"),
- M("parsetree_with_comment", "", "AST with comments"),
+ M("yydebug(+error-tolerant)", "", "yydebug of yacc parser generator"),
+ M("parsetree(+error-tolerant)","", "AST"),
+ M("parsetree_with_comment(+error-tolerant)", "", "AST with comments"),
};
static const struct ruby_opt_message features[] = {
M("gems", "", "rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")"),
@@ -328,10 +337,8 @@ usage(const char *name, int help, int highlight, int columns)
#endif
#if USE_YJIT
static const struct ruby_opt_message yjit_options[] = {
-#if YJIT_STATS
M("--yjit-stats", "", "Enable collecting YJIT statistics"),
-#endif
- M("--yjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: 256)"),
+ M("--yjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: 64)"),
M("--yjit-call-threshold=num", "", "Number of calls to trigger JIT (default: 10)"),
M("--yjit-max-versions=num", "", "Maximum number of versions per basic block (default: 4)"),
M("--yjit-greedy-versioning", "", "Greedy versioning mode (default: disabled)"),
@@ -369,7 +376,7 @@ usage(const char *name, int help, int highlight, int columns)
SHOW(mjit_option_messages[i]);
#endif
#if USE_YJIT
- printf("%s""YJIT options (experimental):%s\n", sb, se);
+ printf("%s""YJIT options:%s\n", sb, se);
for (i = 0; i < numberof(yjit_options); ++i)
SHOW(yjit_options[i]);
#endif
@@ -511,6 +518,8 @@ translit_char_bin(char *p, int from, int to)
#endif
#ifdef _WIN32
+# undef chdir
+# define chdir rb_w32_uchdir
# define UTF8_PATH 1
#endif
@@ -638,8 +647,8 @@ ruby_init_loadpath(void)
# endif
rb_obj_hide(selfpath);
OBJ_FREEZE_RAW(selfpath);
- rb_libruby_selfpath = selfpath;
rb_gc_register_address(&rb_libruby_selfpath);
+ rb_libruby_selfpath = selfpath;
# endif
#endif
@@ -899,14 +908,16 @@ name_match_p(const char *name, const char *str, size_t len)
if (len == 0) return 0;
while (1) {
while (TOLOWER(*str) == *name) {
- if (!--len || !*++str) return 1;
+ if (!--len) return 1;
++name;
+ ++str;
}
if (*str != '-' && *str != '_') return 0;
while (ISALNUM(*name)) name++;
if (*name != '-' && *name != '_') return 0;
++name;
++str;
+ if (--len == 0) return 1;
}
}
@@ -1012,11 +1023,49 @@ debug_option(const char *str, int len, void *arg)
rb_warn("debug features are [%.*s].", (int)strlen(list), list);
}
+static int
+memtermspn(const char *str, char term, int len)
+{
+ RUBY_ASSERT(len >= 0);
+ if (len <= 0) return 0;
+ const char *next = memchr(str, term, len);
+ return next ? (int)(next - str) : len;
+}
+
+static const char additional_opt_sep = '+';
+
+static unsigned int
+dump_additional_option(const char *str, int len, unsigned int bits, const char *name)
+{
+ int w;
+ for (; len-- > 0 && *str++ == additional_opt_sep; len -= w, str += w) {
+ w = memtermspn(str, additional_opt_sep, len);
+#define SET_ADDITIONAL(bit) if (NAME_MATCH_P(#bit, str, w)) { \
+ if (bits & DUMP_BIT(bit)) \
+ rb_warn("duplicate option to dump %s: `%.*s'", name, w, str); \
+ bits |= DUMP_BIT(bit); \
+ continue; \
+ }
+ if (dump_error_tolerant_bits & bits) {
+ SET_ADDITIONAL(error_tolerant);
+ }
+ rb_warn("don't know how to dump %s with `%.*s'", name, w, str);
+ }
+ return bits;
+}
+
static void
dump_option(const char *str, int len, void *arg)
{
static const char list[] = EACH_DUMPS(LITERAL_NAME_ELEMENT, ", ");
-#define SET_WHEN_DUMP(bit) SET_WHEN(#bit, DUMP_BIT(bit), str, len)
+ int w = memtermspn(str, additional_opt_sep, len);
+
+#define SET_WHEN_DUMP(bit) \
+ if (NAME_MATCH_P(#bit, (str), (w))) { \
+ *(unsigned int *)arg |= \
+ dump_additional_option(str + w, len - w, DUMP_BIT(bit), #bit); \
+ return; \
+ }
EACH_DUMPS(SET_WHEN_DUMP, ;);
rb_warn("don't know how to dump `%.*s',", len, str);
rb_warn("but only [%.*s].", (int)strlen(list), list);
@@ -1459,7 +1508,8 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
setup_yjit_options(s);
#else
- rb_warn("Ruby was built without YJIT support");
+ rb_warn("Ruby was built without YJIT support."
+ " You may need to install rustc to build Ruby with YJIT.");
#endif
}
else if (strcmp("yydebug", s) == 0) {
@@ -1563,11 +1613,32 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
rb_warning_category_update(opt->warn.mask, opt->warn.set);
+#if USE_MJIT
+ // rb_call_builtin_inits depends on RubyVM::MJIT.enabled?
+ if (opt->mjit.on)
+ mjit_enabled = true;
+#endif
+
Init_ext(); /* load statically linked extensions before rubygems */
Init_extra_exts();
rb_call_builtin_inits();
ruby_init_prelude();
+ // Initialize JITs after prelude because JITing prelude is typically not optimal.
+#if USE_MJIT
+ // Also, mjit_init is safe only after rb_call_builtin_inits() defines RubyVM::MJIT::Compiler.
+ if (opt->mjit.on)
+ mjit_init(&opt->mjit);
+#endif
+#if USE_YJIT
+ if (opt->yjit)
+ rb_yjit_init();
+#endif
+ // rb_threadptr_root_fiber_setup for the initial thread is called before rb_yjit_enabled_p()
+ // or mjit_enabled becomes true, meaning jit_cont_new is skipped for the initial root fiber.
+ // Therefore we need to call this again here to set the initial root fiber's jit_cont.
+ rb_jit_cont_init(); // must be after mjit_enabled = true and rb_yjit_init()
+
ruby_set_script_name(opt->script_name);
require_libraries(&opt->req_list);
}
@@ -1729,6 +1800,26 @@ copy_str(VALUE str, rb_encoding *enc, bool intern)
return rb_enc_interned_str(RSTRING_PTR(str), RSTRING_LEN(str), enc);
}
+#if USE_YJIT
+// Check that an environment variable is set to a truthy value
+static bool
+env_var_truthy(const char *name)
+{
+ const char *value = getenv(name);
+
+ if (!value)
+ return false;
+ if (strcmp(value, "1") == 0)
+ return true;
+ if (strcmp(value, "true") == 0)
+ return true;
+ if (strcmp(value, "yes") == 0)
+ return true;
+
+ return false;
+}
+#endif
+
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
@@ -1838,7 +1929,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (!(FEATURE_SET_BITS(opt->features) & feature_jit_mask)) {
#if USE_YJIT
- if (!FEATURE_USED_P(opt->features, yjit) && getenv("RUBY_YJIT_ENABLE")) {
+ if (!FEATURE_USED_P(opt->features, yjit) && env_var_truthy("RUBY_YJIT_ENABLE")) {
FEATURE_SET(opt->features, FEATURE_BIT(yjit));
}
#endif
@@ -1850,18 +1941,15 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#if USE_MJIT
if (FEATURE_SET_P(opt->features, mjit)) {
- opt->mjit.on = TRUE; /* set mjit.on for ruby_show_version() API and check to call mjit_init() */
+ opt->mjit.on = true; // set opt->mjit.on for Init_ruby_description() and calling mjit_init()
}
#endif
#if USE_YJIT
if (FEATURE_SET_P(opt->features, yjit)) {
- rb_yjit_init();
+ opt->yjit = true; // set opt->yjit for Init_ruby_description() and calling rb_yjit_init()
}
#endif
-#if USE_MJIT
- mjit_opts.on = opt->mjit.on; /* used by Init_ruby_description(). mjit_init() still can't be called here. */
-#endif
- Init_ruby_description();
+ Init_ruby_description(opt);
if (opt->dump & (DUMP_BIT(version) | DUMP_BIT(version_v))) {
ruby_show_version();
if (opt->dump & DUMP_BIT(version)) return Qtrue;
@@ -1915,12 +2003,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ruby_gc_set_params();
ruby_init_loadpath();
-#if USE_MJIT
- if (opt->mjit.on)
- /* Using TMP_RUBY_PREFIX created by ruby_init_loadpath(). */
- mjit_init(&opt->mjit);
-#endif
-
Init_enc();
lenc = rb_locale_encoding();
rb_enc_associate(rb_progname, lenc);
@@ -1929,6 +2011,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (opt->dump & DUMP_BIT(yydebug)) {
rb_parser_set_yydebug(parser, Qtrue);
}
+ if (opt->dump & DUMP_BIT(error_tolerant)) {
+ rb_parser_error_tolerant(parser);
+ }
if (opt->ext.enc.name != 0) {
opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
}
@@ -2021,11 +2106,11 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ruby_set_argv(argc, argv);
process_sflag(&opt->sflag);
- rb_parser_set_context(parser, 0, TRUE);
-
if (opt->e_script) {
VALUE progname = rb_progname;
rb_encoding *eenc;
+ rb_parser_set_context(parser, 0, TRUE);
+
if (opt->src.enc.index >= 0) {
eenc = rb_enc_from_index(opt->src.enc.index);
}
@@ -2050,6 +2135,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
else {
VALUE f;
f = open_load_file(script_name, &opt->xflag);
+ rb_parser_set_context(parser, 0, f == rb_stdin);
ast = load_file(parser, opt->script_name, f, 1, opt);
}
ruby_set_script_name(opt->script_name);
diff --git a/rubystub.c b/rubystub.c
index e7f46e78a5..75aeca1869 100644
--- a/rubystub.c
+++ b/rubystub.c
@@ -1,4 +1,5 @@
#include "internal.h"
+#include "internal/missing.h"
#if defined HAVE_DLADDR
#include <dlfcn.h>
#endif
diff --git a/sample/coverage.rb b/sample/coverage.rb
index 8e8d6167e2..42ba89fd50 100644
--- a/sample/coverage.rb
+++ b/sample/coverage.rb
@@ -49,7 +49,7 @@ at_exit do
end
end
- open(cfile, "w") do |out|
+ File.open(cfile, "w") do |out|
covs.zip(sources, pcovs).each_with_index do |(cov, line, pcov), idx|
cov += pcov || 0 if cov
cov = (cov ? (cov == 0 ? "#####" : cov.to_s) : "-").rjust(9)
diff --git a/sample/from.rb b/sample/from.rb
index db1299c869..0e5a08de5f 100644
--- a/sample/from.rb
+++ b/sample/from.rb
@@ -62,7 +62,7 @@ def from_main
if File.exist?(file)
atime = File.atime(file)
mtime = File.mtime(file)
- open(file, "r") do |f|
+ File.open(file, "r") do |f|
until f.eof?
header = {}
f.each_line do |line|
diff --git a/sample/mine.rb b/sample/mine.rb
index a841d1a60a..77e0204bf6 100755
--- a/sample/mine.rb
+++ b/sample/mine.rb
@@ -1,6 +1,8 @@
#! /usr/bin/ruby -Ku
# -*- coding: utf-8 -*-
+require 'io/console'
+
class Board
def clr
print "\e[2J"
@@ -143,8 +145,8 @@ class Board
end
bd=Board.new(10,10,10)
-system("stty raw -echo")
-begin
+
+IO.console.raw do
loop do
case STDIN.getc
when ?n # new game
@@ -170,7 +172,5 @@ begin
bd.reset
end
end
-ensure
- system("stty -raw echo")
end
print "\n"
diff --git a/sample/mpart.rb b/sample/mpart.rb
index a88eba0ef6..eeb895d3de 100644
--- a/sample/mpart.rb
+++ b/sample/mpart.rb
@@ -2,11 +2,29 @@
# split into multi part
# usage: mpart.rb [-nnn] file..
+class MPart < File
+ def self.new(basename, extname, part, parts)
+ super(sprintf("%s.%s%02d", basename, extname, part), "w").
+ begin_mpart(basename, part, parts)
+ end
+
+ def begin_mpart(basename, part, parts)
+ printf("%s part%02d/%02d\n", basename, part, parts)
+ write("BEGIN--cut here--cut here\n")
+ self
+ end
+
+ def close
+ write("END--cut here--cut here\n")
+ super
+ end
+end
+
lines = 1000
if (ARGV[0] =~ /^-(\d+)$/ )
- lines = $1.to_i;
- ARGV.shift;
+ lines = $1.to_i
+ ARGV.shift
end
basename = ARGV[0]
@@ -14,31 +32,23 @@ extname = "part"
part = 1
line = 0
+ofp = nil
fline = 0
-for i in ifp = open(basename)
- fline = fline + 1
-end
-ifp.close
+File.foreach(basename) {fline += 1}
parts = fline / lines + 1
-for i in ifp = open(basename)
+File.foreach(basename) do |i|
if line == 0
- ofp = open(sprintf("%s.%s%02d", basename, extname, part), "w")
- printf(ofp, "%s part%02d/%02d\n", basename, part, parts)
- ofp.write("BEGIN--cut here--cut here\n")
+ ofp = MPart.new(basename, extname, part, parts)
end
ofp.write(i)
- line = line + 1
- if line >= lines and !ifp.eof?
- ofp.write("END--cut here--cut here\n")
+ line += 1
+ if line >= lines
ofp.close
- part = part + 1
+ part += 1
line = 0
end
end
-ofp.write("END--cut here--cut here\n")
ofp.close
-
-ifp.close
diff --git a/sample/trick2018/02-mame/entry.rb b/sample/trick2018/02-mame/entry.rb
index cc4ef9cbc4..ced791aa3d 100644
--- a/sample/trick2018/02-mame/entry.rb
+++ b/sample/trick2018/02-mame/entry.rb
@@ -1,11 +1,11 @@
'';eval(r=%q(->z{r="'';eval(r=\
-%q(#{r}))[%q`#{z}`]";i=-040;30.
+%q(#{r}))[%q`#{z}`]";i=-040;31.
times{|n|(15+n%2*15-n/2).times{
r<<r[i+=(1.-n&2)*(32-n%2*31)]}}
i=r[524,0]=?\0;eval(r[479..-1])
c['"']}))[%q`GFEDCBA"+"[e\"'"'t
kE*;;\";" TRICK2018 ";tb,;{{r
-2E0$ob[us@*0)[90,336])_#i\n}s#i
+2E0$ob[us@*0)[90,336])#_i\n}s#i
0H}>["t]];};o[1,?\n*8];ex"-}eac
1Hl<1[-1]*2*t=n%2];o[14-n,0)mvk
8M$<4,?\n];15.times{|n|;o[35ie2
diff --git a/sample/trick2022/01-tompng/Gemfile b/sample/trick2022/01-tompng/Gemfile
new file mode 100644
index 0000000000..982b9de67f
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'matrix'
diff --git a/sample/trick2022/01-tompng/Gemfile.lock b/sample/trick2022/01-tompng/Gemfile.lock
new file mode 100644
index 0000000000..8bb3c69025
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile.lock
@@ -0,0 +1,13 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ matrix (0.4.2)
+
+PLATFORMS
+ x86_64-darwin-20
+
+DEPENDENCIES
+ matrix
+
+BUNDLED WITH
+ 2.3.3
diff --git a/sample/trick2022/01-tompng/authors.markdown b/sample/trick2022/01-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/01-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/01-tompng/entry.rb b/sample/trick2022/01-tompng/entry.rb
new file mode 100644
index 0000000000..97beacc684
--- /dev/null
+++ b/sample/trick2022/01-tompng/entry.rb
@@ -0,0 +1,40 @@
+ eval((s=%~c=(0..35
+ ).map{s[2*_1+1]}*'';class$Inte
+ ger;def$quXinclude(Math ;spXo(a)=self*
+ a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
+ def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
+ =self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
+ a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
+ ;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
+ 'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
+ 5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
+ |i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
+ *?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
+ W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
+ ?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
+ =(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
+ 34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
+ p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
+dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
+15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
+)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
+MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
+[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
+44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
+ |i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
+ v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
+ ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
+ ).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
+ ,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
+ =(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
+ .powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
+ ,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
+ (j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
+ 0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
+ .lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
+ (v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
+ s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
+ =z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
+ t]}};eval(Xfg K#R N bp-E_Xc)~.split(
+ ?X);(0..36).map{s[2*_1].split}
+ .join.tr('$',$/)))
diff --git a/sample/trick2022/01-tompng/remarks.markdown b/sample/trick2022/01-tompng/remarks.markdown
new file mode 100644
index 0000000000..70601908b7
--- /dev/null
+++ b/sample/trick2022/01-tompng/remarks.markdown
@@ -0,0 +1,51 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+Or run it with one non-ascii half-width character argument:
+
+ ruby entry.rb ⬮
+ ruby entry.rb 𓆡
+
+I confirmed the following implementations/platforms:
+
+* ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
+* ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+
+### Description
+
+This program is an aquatic quine.
+Some characters in the code are overwritten with `" "`, but this program can restore the missing parts.
+Every frame of this animation is an executable ruby program that let fishes start swimming again from their current position.
+
+### Internals
+
+#### Error Correction
+
+Error correction is performed for each block of length 135.
+It consists of 89 kinds of characters(`[*('!'..'W'), '[', *(']'..'}')]`) and satisfies the following constraint.
+
+```
+matrix(size: 45x135) * block_vector(size: 135) % 89 == zero_vector(size: 45)
+```
+
+To restore the missing characters in the block, we need to solve a linear equation problem in modulo 89.
+This can be achieved by using bundled gem 'matrix' and overwriting some methods.
+
+```ruby
+require 'matrix'
+matrix = Matrix[[3, 1, 4], [1, 5, 9], [2, 6, 5]]
+class Integer
+ def quo(x) = self * x.pow(87, 89) % 89 # Fermat's little theorem. 89 is a prime number.
+ def abs() = [self % 89, 89 - self % 89].min # To avoid division by multiple of 89.
+end
+answer = matrix.lup.solve([1, 2, 3]) #=> Vector[24, 42, 83]
+(matrix * answer).map { _1 % 89 } #=> Vector[1, 2, 3]
+```
+
+#### Resuming Animation
+
+The entire animation of this fish tank is a loop of 960 frames.
+This program uses position of the floating bubbles to detect current frame number from the executed source code.
diff --git a/sample/trick2022/02-tompng/authors.markdown b/sample/trick2022/02-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/02-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/02-tompng/entry.rb b/sample/trick2022/02-tompng/entry.rb
new file mode 100644
index 0000000000..2e2e2bcf74
--- /dev/null
+++ b/sample/trick2022/02-tompng/entry.rb
@@ -0,0 +1,32 @@
+ q=->{!sleep _1/1e2};p=(
+ c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]}
+ require'socket';puts'op' "en http://localhost:#{(
+ w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
+ ]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
+ ].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t-
+ 9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y=
+(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])[
+x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1-
+a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(y
+.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatten
+redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '+
+"200 OK\r\nContent-" 'T' "ype:text/html\r\n\r\n";r['/ ']?s.
+ <<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'+
+ 'th:252px;}</styl' 'e>' '<form target="i"><input src="'+
+ "g#{rand}\" type" '="im' 'age"><iframe name="i"></ifra'+
+ 'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<<
+ h+'GIF8' '7a'+[84,
+ 84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1%
+ 3)*17}].pack('v3c*'); loop{ s<<[67434785,5,
+ 44,84,84,7,c.map{_1* 127} .each_slice(126
+ ).map{[127,128,*_1 ] .pack'c*'}*'',
+ 1,129].pack('V3x' 'v2na*c2x');q[
+ 5];q.[]1while(r ==r=c)}):(x,y,
+ z=r.scan(/\d+/).map{_1.to_f/
+ 126-1};z&&p<<[rand-0.5,(
+ z=x+y.i)*1.5,z/(z.
+ abs+0.9),0,-p[
+ -3][4]=-1]
+ s.<<h);s
+ .close
+ }}
diff --git a/sample/trick2022/02-tompng/remarks.markdown b/sample/trick2022/02-tompng/remarks.markdown
new file mode 100644
index 0000000000..3b2d3fd84b
--- /dev/null
+++ b/sample/trick2022/02-tompng/remarks.markdown
@@ -0,0 +1,32 @@
+### Remarks
+
+1. Run it with `ruby entry.rb 8080`
+
+2. Open "http://localhost:8080"
+
+3. Click on the screen and interact with it
+
+I confirmed the following implementations/platforms:
+
+* Ruby Version
+ * ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+* Browser
+ * Chrome(macOS, Android)
+ * Firefox(macOS)
+ * Edge(macOS)
+
+### Description
+
+This program is an HTTP server that provides a fractal creature playground.
+You can see the heartbeat of a mysterious fractal creature. Clicking on the screen will change the shape of the creature.
+Surprisingly, this interactive webpage is built without JavaScript.
+
+### Internals
+
+Fractal: Iterated function system
+Rendering from server: Streaming animated GIF
+Sending click event to server: `<input type="image" src="streaming.gif">` with `<form target="invisible_iframe">`
+
+### Limitations
+
+Does not work on Safari and iOS.
diff --git a/sample/trick2022/03-mame/authors.markdown b/sample/trick2022/03-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2022/03-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2022/03-mame/entry.rb b/sample/trick2022/03-mame/entry.rb
new file mode 100644
index 0000000000..f24595dfa9
--- /dev/null
+++ b/sample/trick2022/03-mame/entry.rb
@@ -0,0 +1,27 @@
+2022;"#
+
+.chars} {puts'TRICK+2022'
+ \ { ;
+#';$><< b
+ ?!
+};#{s=' ' # 0
+! s[0]? ( b=$<.read ;'
+} ub( ,''}
+';a= ''<<32
+b.lines {puts( ?.. *(
+b.size) .gsub(/./) {
+b.sub!( /^#$`\K(\S)
+ /x,a)?$1:a }
+ .rstrip)}):
+ ( [ 12,1,12,11].
+cycle { | i | t = ( s *
+
+10<<
+10)*
+10+ %(\e[A)*
+10
+10. times{t[i*
+ _1 ] = 'TRICK+2022'[
+ _1 ] };$><<t
+ sleep 1})
+ }"
diff --git a/sample/trick2022/03-mame/remarks.markdown b/sample/trick2022/03-mame/remarks.markdown
new file mode 100644
index 0000000000..c38279f016
--- /dev/null
+++ b/sample/trick2022/03-mame/remarks.markdown
@@ -0,0 +1,96 @@
+Execute the program normally.
+
+```
+$ ruby entry.rb
+```
+
+It shakes a string.
+
+... Wait! This is not all.
+
+Next, please apply "leftward gravity" to each letter in the file.
+IOW, if there is a space to the left of a letter, move it to the left.
+Here, you may want to use the following command.
+
+```
+$ sed "s/ //g" entry.rb | tee up.rb
+```
+
+This program applies "upward gravity" to each letter in an input text.
+The following demo will help you understand what this means.
+
+```
+$ cat test.txt
+$ ruby up.rb test.txt
+```
+
+Now, here's where we come in.
+Please apply "upward gravity" to entry.rb.
+
+```
+$ ruby up.rb entry.rb | tee left.rb
+```
+
+I think that you already noticed that.
+This program applies "leftward gravity" to an input text.
+
+```
+$ cat test.txt
+$ ruby left.rb test.txt
+```
+
+`sed` is no longer required to create `up.rb`; just use `left.rb`.
+
+```
+$ ruby left.rb entry.rb > up.rb
+```
+
+We've come to the final stage.
+Please apply `left.rb` to `left.rb`.
+
+```
+$ ruby left.rb left.rb | tee horizontal.rb
+$ ruby horizontal.rb
+```
+
+Of course, it is also possible to apply `up.rb` to `up.rb`.
+
+```
+$ ruby up.rb up.rb | tee vertical.rb
+$ ruby vertical.rb
+```
+
+Can you tell how they work? Enjoy analyzing!
+
+
+
+---
+Code reading tips (spoiler)
+
+Some code fragments are highly reused between the programs.
+For example, note that this program has one code fragment to input a text
+(`b=$>.read`); `up.rb` and `left.rb` share and use this code fragment.
+Also, `horizontal.rb` and `vertical.rb` share the fragment `puts'TRICK+2022'`.
+Sometimes letters in very distant places are reused all over the place.
+
+Can you tell how it detects if it is already aligned or not yet?
+Here is a simplified version of the gimmick to switch behavior when
+left-aligned:
+
+```
+"\ #{puts('not left-aligned yet')}
+ # {puts('left-aligned')}"
+```
+
+And for top-aligned:
+
+```
+"#
+xx{puts('top-aligned')}
+x#{puts('not top-aligned yet')}
+"
+```
+
+It is also necessary to detect "top-left-aligned" and "left-top-aligned".
+I made tons of subtle adjustments and trial-and-error to create the program.
+I no longer know precisely how it works.
diff --git a/sample/trick2022/03-mame/test.txt b/sample/trick2022/03-mame/test.txt
new file mode 100644
index 0000000000..18802153a9
--- /dev/null
+++ b/sample/trick2022/03-mame/test.txt
@@ -0,0 +1,13 @@
+T
+ R
+ I
+ C
+ K
+ |
+ |
+ |
+ |
+ 2
+ 0
+ 2
+ 2
diff --git a/sample/trick2022/README.md b/sample/trick2022/README.md
new file mode 100644
index 0000000000..3b2af6f86b
--- /dev/null
+++ b/sample/trick2022/README.md
@@ -0,0 +1,14 @@
+This directory contains the award-winning entries of
+the 4th Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2022).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 01-tompng/entry.rb: "Best fishbowl" -- Tomoya Ishida (tompng)
+* 02-tompng/entry.rb: "Most interactive code" -- Tomoya Ishida (tompng)
+* 03-mame/entry.rb: "Most anti-gravity" -- Yusuke Endoh
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2022
diff --git a/sample/uumerge.rb b/sample/uumerge.rb
index 2576bcb864..1b81582c24 100644
--- a/sample/uumerge.rb
+++ b/sample/uumerge.rb
@@ -15,7 +15,7 @@ while line = gets()
if out_stdout
out = STDOUT
else
- out = open($file, "w") if $file != ""
+ out = File.open($file, "w") if $file != ""
end
out.binmode
break
diff --git a/scheduler.c b/scheduler.c
index 06658356b1..4be18e1799 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -28,10 +28,63 @@ static ID id_process_wait;
static ID id_io_read, id_io_pread;
static ID id_io_write, id_io_pwrite;
static ID id_io_wait;
+static ID id_io_select;
static ID id_io_close;
static ID id_address_resolve;
+static ID id_fiber_schedule;
+
+/*
+ * Document-class: Fiber::Scheduler
+ *
+ * This is not an existing class, but documentation of the interface that Scheduler
+ * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking
+ * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations
+ * of some concepts.
+ *
+ * Scheduler's behavior and usage are expected to be as follows:
+ *
+ * * When the execution in the non-blocking Fiber reaches some blocking operation (like
+ * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's
+ * hook methods, listed below.
+ * * Scheduler somehow registers what the current fiber is waiting on, and yields control
+ * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its
+ * wait to end, and other fibers in the same thread can perform)
+ * * At the end of the current thread execution, the scheduler's method #scheduler_close is called
+ * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has
+ * registered on hook calls) and resuming them when the awaited resource is ready
+ * (e.g. I/O ready or sleep time elapsed).
+ *
+ * This way concurrent execution will be achieved transparently for every
+ * individual Fiber's code.
+ *
+ * Scheduler implementations are provided by gems, like
+ * Async[https://github.com/socketry/async].
+ *
+ * Hook methods are:
+ *
+ * * #io_wait, #io_read, #io_write, #io_pread, #io_pwrite, and #io_select, #io_close
+ * * #process_wait
+ * * #kernel_sleep
+ * * #timeout_after
+ * * #address_resolve
+ * * #block and #unblock
+ * * (the list is expanded as Ruby developers make more methods having non-blocking calls)
+ *
+ * When not specified otherwise, the hook implementations are mandatory: if they are not
+ * implemented, the methods trying to call hook will fail. To provide backward compatibility,
+ * in the future hooks will be optional (if they are not implemented, due to the scheduler
+ * being created for the older Ruby version, the code which needs this hook will not fail,
+ * and will just behave in a blocking fashion).
+ *
+ * It is also strongly recommended that the scheduler implements the #fiber method, which is
+ * delegated to by Fiber.schedule.
+ *
+ * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in
+ * <tt>test/fiber/scheduler.rb</tt>
+ *
+ */
void
Init_Fiber_Scheduler(void)
{
@@ -51,9 +104,30 @@ Init_Fiber_Scheduler(void)
id_io_pwrite = rb_intern_const("io_pwrite");
id_io_wait = rb_intern_const("io_wait");
+ id_io_select = rb_intern_const("io_select");
id_io_close = rb_intern_const("io_close");
id_address_resolve = rb_intern_const("address_resolve");
+
+ id_fiber_schedule = rb_intern_const("fiber");
+
+#if 0 /* for RDoc */
+ rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "Scheduler", rb_cObject);
+ rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_close, 0);
+ rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_process_wait, 2);
+ rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_io_wait, 3);
+ rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_io_read, 4);
+ rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_io_write, 4);
+ rb_define_method(rb_cFiberScheduler, "io_pread", rb_fiber_scheduler_io_pread, 5);
+ rb_define_method(rb_cFiberScheduler, "io_pwrite", rb_fiber_scheduler_io_pwrite, 5);
+ rb_define_method(rb_cFiberScheduler, "io_select", rb_fiber_scheduler_io_select, 4);
+ rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_kernel_sleep, 1);
+ rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_address_resolve, 1);
+ rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_timeout_after, 3);
+ rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_block, 2);
+ rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_unblock, 2);
+ rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler, -2);
+#endif
}
VALUE
@@ -87,6 +161,21 @@ verify_interface(VALUE scheduler)
}
}
+static VALUE
+fiber_scheduler_close(VALUE scheduler)
+{
+ return rb_fiber_scheduler_close(scheduler);
+}
+
+static VALUE
+fiber_scheduler_close_ensure(VALUE _thread)
+{
+ rb_thread_t *thread = (rb_thread_t*)_thread;
+ thread->scheduler = Qnil;
+
+ return Qnil;
+}
+
VALUE
rb_fiber_scheduler_set(VALUE scheduler)
{
@@ -99,9 +188,13 @@ rb_fiber_scheduler_set(VALUE scheduler)
verify_interface(scheduler);
}
- // We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler.
+ // We invoke Scheduler#close when setting it to something else, to ensure
+ // the previous scheduler runs to completion before changing the scheduler.
+ // That way, we do not need to consider interactions, e.g., of a Fiber from
+ // the previous scheduler with the new scheduler.
if (thread->scheduler != Qnil) {
- rb_fiber_scheduler_close(thread->scheduler);
+ // rb_fiber_scheduler_close(thread->scheduler);
+ rb_ensure(fiber_scheduler_close, thread->scheduler, fiber_scheduler_close_ensure, (VALUE)thread);
}
thread->scheduler = scheduler;
@@ -133,6 +226,16 @@ VALUE rb_fiber_scheduler_current_for_thread(VALUE thread)
return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread));
}
+/*
+ *
+ * Document-method: Fiber::Scheduler#close
+ *
+ * Called when the current thread exits. The scheduler is expected to implement this
+ * method in order to allow all waiting fibers to finalize their execution.
+ *
+ * The suggested pattern is to implement the main event loop in the #close method.
+ *
+ */
VALUE
rb_fiber_scheduler_close(VALUE scheduler)
{
@@ -140,11 +243,17 @@ rb_fiber_scheduler_close(VALUE scheduler)
VALUE result;
+ // The reason for calling `scheduler_close` before calling `close` is for
+ // legacy schedulers which implement `close` and expect the user to call
+ // it. Subsequently, that method would call `Fiber.set_scheduler(nil)`
+ // which should call `scheduler_close`. If it were to call `close`, it
+ // would create an infinite loop.
+
result = rb_check_funcall(scheduler, id_scheduler_close, 0, NULL);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
result = rb_check_funcall(scheduler, id_close, 0, NULL);
- if (result != Qundef) return result;
+ if (!UNDEF_P(result)) return result;
return Qnil;
}
@@ -159,6 +268,17 @@ rb_fiber_scheduler_make_timeout(struct timeval *timeout)
return Qnil;
}
+/*
+ * Document-method: Fiber::Scheduler#kernel_sleep
+ * call-seq: kernel_sleep(duration = nil)
+ *
+ * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide
+ * an implementation of sleeping in a non-blocking way. Implementation might
+ * register the current fiber in some list of "which fiber wait until what
+ * moment", call Fiber.yield to pass control, and then in #close resume
+ * the fibers whose wait period has elapsed.
+ *
+ */
VALUE
rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE timeout)
{
@@ -172,6 +292,34 @@ rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv)
}
#if 0
+/*
+ * Document-method: Fiber::Scheduler#timeout_after
+ * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block
+ *
+ * Invoked by Timeout.timeout to execute the given +block+ within the given
+ * +duration+. It can also be invoked directly by the scheduler or user code.
+ *
+ * Attempt to limit the execution time of a given +block+ to the given
+ * +duration+ if possible. When a non-blocking operation causes the +block+'s
+ * execution time to exceed the specified +duration+, that non-blocking
+ * operation should be interrupted by raising the specified +exception_class+
+ * constructed with the given +exception_arguments+.
+ *
+ * General execution timeouts are often considered risky. This implementation
+ * will only interrupt non-blocking operations. This is by design because it's
+ * expected that non-blocking operations can fail for a variety of
+ * unpredictable reasons, so applications should already be robust in handling
+ * these conditions and by implication timeouts.
+ *
+ * However, as a result of this design, if the +block+ does not invoke any
+ * non-blocking operations, it will be impossible to interrupt it. If you
+ * desire to provide predictable points for timeouts, consider adding
+ * +sleep(0)+.
+ *
+ * If the block is executed successfully, its result will be returned.
+ *
+ * The exception will typically be raised using Fiber#raise.
+ */
VALUE
rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message)
{
@@ -189,6 +337,24 @@ rb_fiber_scheduler_timeout_afterv(VALUE scheduler, int argc, VALUE * argv)
}
#endif
+/*
+ * Document-method: Fiber::Scheduler#process_wait
+ * call-seq: process_wait(pid, flags)
+ *
+ * Invoked by Process::Status.wait in order to wait for a specified process.
+ * See that method description for arguments description.
+ *
+ * Suggested minimal implementation:
+ *
+ * Thread.new do
+ * Process::Status.wait(pid, flags)
+ * end.value
+ *
+ * This hook is optional: if it is not present in the current scheduler,
+ * Process::Status.wait will behave as a blocking method.
+ *
+ * Expected to return a Process::Status instance.
+ */
VALUE
rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
{
@@ -199,20 +365,74 @@ rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
return rb_check_funcall(scheduler, id_process_wait, 2, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#block
+ * call-seq: block(blocker, timeout = nil)
+ *
+ * Invoked by methods like Thread.join, and by Mutex, to signify that current
+ * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has
+ * elapsed.
+ *
+ * +blocker+ is what we are waiting on, informational only (for debugging and
+ * logging). There are no guarantee about its value.
+ *
+ * Expected to return boolean, specifying whether the blocking operation was
+ * successful or not.
+ */
VALUE
rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
{
return rb_funcall(scheduler, id_block, 2, blocker, timeout);
}
+/*
+ * Document-method: Fiber::Scheduler#unblock
+ * call-seq: unblock(blocker, fiber)
+ *
+ * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock
+ * calls #block and Mutex#unlock calls #unblock). The scheduler should use
+ * the +fiber+ parameter to understand which fiber is unblocked.
+ *
+ * +blocker+ is what was awaited for, but it is informational only (for debugging
+ * and logging), and it is not guaranteed to be the same value as the +blocker+ for
+ * #block.
+ *
+ */
VALUE
rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
{
VM_ASSERT(rb_obj_is_fiber(fiber));
- return rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+ // `rb_fiber_scheduler_unblock` can be called from points where `errno` is expected to be preserved. Therefore, we should save and restore it. For example `io_binwrite` calls `rb_fiber_scheduler_unblock` and if `errno` is reset to 0 by user code, it will break the error handling in `io_write`.
+ // If we explicitly preserve `errno` in `io_binwrite` and other similar functions (e.g. by returning it), this code is no longer needed. I hope in the future we will be able to remove it.
+ int saved_errno = errno;
+
+ VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+
+ errno = saved_errno;
+
+ return result;
}
+/*
+ * Document-method: Fiber::Scheduler#io_wait
+ * call-seq: io_wait(io, events, timeout)
+ *
+ * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the
+ * specified descriptor is ready for specified events within
+ * the specified +timeout+.
+ *
+ * +events+ is a bit mask of <tt>IO::READABLE</tt>, <tt>IO::WRITABLE</tt>, and
+ * <tt>IO::PRIORITY</tt>.
+ *
+ * Suggested implementation should register which Fiber is waiting for which
+ * resources and immediately calling Fiber.yield to pass control to other
+ * fibers. Then, in the #close method, the scheduler might dispatch all the
+ * I/O resources to fibers waiting for it.
+ *
+ * Expected to return the subset of events that are ready immediately.
+ *
+ */
VALUE
rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout)
{
@@ -222,53 +442,151 @@ rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeou
VALUE
rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io)
{
- return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_READABLE), Qnil);
+ return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_READABLE), rb_io_timeout(io));
}
VALUE
rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io)
{
- return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), Qnil);
+ return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), rb_io_timeout(io));
+}
+
+/*
+ * Document-method: Fiber::Scheduler#io_select
+ * call-seq: io_select(readables, writables, exceptables, timeout)
+ *
+ * Invoked by IO.select to ask whether the specified descriptors are ready for
+ * specified events within the specified +timeout+.
+ *
+ * Expected to return the 3-tuple of Array of IOs that are ready.
+ *
+ */
+VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout)
+{
+ VALUE arguments[] = {
+ readables, writables, exceptables, timeout
+ };
+
+ return rb_fiber_scheduler_io_selectv(scheduler, 4, arguments);
+}
+
+VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv)
+{
+ // I wondered about extracting argv, and checking if there is only a single
+ // IO instance, and instead calling `io_wait`. However, it would require a
+ // decent amount of work and it would be hard to preserve the exact
+ // semantics of IO.select.
+
+ return rb_check_funcall(scheduler, id_io_select, argc, argv);
}
+/*
+ * Document-method: Fiber::Scheduler#io_read
+ * call-seq: io_read(io, buffer, minimum_length) -> read length or -errno
+ *
+ * Invoked by IO#read or IO#Buffer.read to read +length+ bytes from +io+ into a
+ * specified +buffer+ (see IO::Buffer).
+ *
+ * The +minimum_length+ argument is the "minimum length to be read". If the IO
+ * buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to 8KiB might be
+ * read, but at least 1KiB will be. Generally, the only case where less data
+ * than +length+ will be read is if there is an error reading the data.
+ *
+ * Specifying a +length+ of 0 is valid and means try reading at least once and
+ * return any available data.
+ *
+ * Suggested implementation should try to read from +io+ in a non-blocking
+ * manner and call #io_wait if the +io+ is not ready (which will yield control
+ * to other fibers).
+ *
+ * See IO::Buffer for an interface available to return data.
+ *
+ * Expected to return number of bytes read, or, in case of an error,
+ * <tt>-errno</tt> (negated number corresponding to system's error code).
+ *
+ * The method should be considered _experimental_.
+ */
VALUE
-rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
+rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length)
+ io, buffer, SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_read, 3, arguments);
+ return rb_check_funcall(scheduler, id_io_read, 4, arguments);
}
+
+/*
+ * Document-method: Fiber::Scheduler#io_read
+ * call-seq: io_pread(io, buffer, from, length, offset) -> read length or -errno
+ *
+ * Invoked by IO::Buffer#pread. See that method for description of arguments.
+ *
+ */
VALUE
-rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
+rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length), OFFT2NUM(offset)
+ io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_pread, 4, arguments);
+ return rb_check_funcall(scheduler, id_io_pread, 5, arguments);
}
+/*
+ * Document-method: Scheduler#io_write
+ * call-seq: io_write(io, buffer, minimum_length) -> written length or -errno
+ *
+ * Invoked by IO#write or IO::Buffer#write to write +length+ bytes to +io+ from
+ * from a specified +buffer+ (see IO::Buffer).
+ *
+ * The +minimum_length+ argument is the "minimum length to be written". If the
+ * IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB), at most
+ * 8KiB will be written, but at least 1KiB will be. Generally, the only case
+ * where less data than +minimum_length+ will be written is if there is an
+ * error writing the data.
+ *
+ * Specifying a +length+ of 0 is valid and means try writing at least once, as
+ * much data as possible.
+ *
+ * Suggested implementation should try to write to +io+ in a non-blocking
+ * manner and call #io_wait if the +io+ is not ready (which will yield control
+ * to other fibers).
+ *
+ * See IO::Buffer for an interface available to get data from buffer
+ * efficiently.
+ *
+ * Expected to return number of bytes written, or, in case of an error,
+ * <tt>-errno</tt> (negated number corresponding to system's error code).
+ *
+ * The method should be considered _experimental_.
+ */
VALUE
-rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
+rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length)
+ io, buffer, SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_write, 3, arguments);
+ return rb_check_funcall(scheduler, id_io_write, 4, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#io_pwrite
+ * call-seq: io_pwrite(io, buffer, from, length, offset) -> written length or -errno
+ *
+ * Invoked by IO::Buffer#pwrite. See that method for description of arguments.
+ *
+ */
VALUE
-rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
+rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
{
VALUE arguments[] = {
- io, buffer, SIZET2NUM(length), OFFT2NUM(offset)
+ io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset)
};
- return rb_check_funcall(scheduler, id_io_pwrite, 4, arguments);
+ return rb_check_funcall(scheduler, id_io_pwrite, 5, arguments);
}
VALUE
@@ -276,7 +594,7 @@ rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t
{
VALUE buffer = rb_io_buffer_new(base, size, RB_IO_BUFFER_LOCKED);
- VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length);
+ VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length, 0);
rb_io_buffer_unlock(buffer);
rb_io_buffer_free(buffer);
@@ -289,7 +607,7 @@ rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base,
{
VALUE buffer = rb_io_buffer_new((void*)base, size, RB_IO_BUFFER_LOCKED|RB_IO_BUFFER_READONLY);
- VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length);
+ VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length, 0);
rb_io_buffer_unlock(buffer);
rb_io_buffer_free(buffer);
@@ -305,6 +623,38 @@ rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io)
return rb_check_funcall(scheduler, id_io_close, 1, arguments);
}
+/*
+ * Document-method: Fiber::Scheduler#address_resolve
+ * call-seq: address_resolve(hostname) -> array_of_strings or nil
+ *
+ * Invoked by any method that performs a non-reverse DNS lookup. The most
+ * notable method is Addrinfo.getaddrinfo, but there are many other.
+ *
+ * The method is expected to return an array of strings corresponding to ip
+ * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved.
+ *
+ * Fairly exhaustive list of all possible call-sites:
+ *
+ * - Addrinfo.getaddrinfo
+ * - Addrinfo.tcp
+ * - Addrinfo.udp
+ * - Addrinfo.ip
+ * - Addrinfo.new
+ * - Addrinfo.marshal_load
+ * - SOCKSSocket.new
+ * - TCPServer.new
+ * - TCPSocket.new
+ * - IPSocket.getaddress
+ * - TCPSocket.gethostbyname
+ * - UDPSocket#connect
+ * - UDPSocket#bind
+ * - UDPSocket#send
+ * - Socket.getaddrinfo
+ * - Socket.gethostbyname
+ * - Socket.pack_sockaddr_in
+ * - Socket.sockaddr_in
+ * - Socket.unpack_sockaddr_in
+ */
VALUE
rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
{
@@ -314,3 +664,24 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
return rb_check_funcall(scheduler, id_address_resolve, 1, arguments);
}
+
+/*
+ * Document-method: Fiber::Scheduler#fiber
+ * call-seq: fiber(&block)
+ *
+ * Implementation of the Fiber.schedule. The method is <em>expected</em> to immediately
+ * run the given block of code in a separate non-blocking fiber, and to return that Fiber.
+ *
+ * Minimal suggested implementation is:
+ *
+ * def fiber(&block)
+ * fiber = Fiber.new(blocking: false, &block)
+ * fiber.resume
+ * fiber
+ * end
+ */
+VALUE
+rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat)
+{
+ return rb_funcall_passing_block_kw(scheduler, id_fiber_schedule, argc, argv, kw_splat);
+}
diff --git a/shape.c b/shape.c
new file mode 100644
index 0000000000..1dc08dcb60
--- /dev/null
+++ b/shape.c
@@ -0,0 +1,825 @@
+#include "vm_core.h"
+#include "vm_sync.h"
+#include "shape.h"
+#include "gc.h"
+#include "symbol.h"
+#include "id_table.h"
+#include "internal/class.h"
+#include "internal/symbol.h"
+#include "internal/variable.h"
+#include "variable.h"
+#include <stdbool.h>
+
+#ifndef SHAPE_DEBUG
+#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
+#endif
+
+static ID id_frozen;
+static ID id_t_object;
+static ID size_pool_edge_names[SIZE_POOL_COUNT];
+
+/*
+ * Shape getters
+ */
+rb_shape_t *
+rb_shape_get_root_shape(void)
+{
+ return GET_VM()->root_shape;
+}
+
+shape_id_t
+rb_shape_id(rb_shape_t * shape)
+{
+ return (shape_id_t)(shape - GET_VM()->shape_list);
+}
+
+bool
+rb_shape_root_shape_p(rb_shape_t* shape)
+{
+ return shape == rb_shape_get_root_shape();
+}
+
+void
+rb_shape_each_shape(each_shape_callback callback, void *data)
+{
+ rb_shape_t *cursor = rb_shape_get_root_shape();
+ rb_shape_t *end = rb_shape_get_shape_by_id(GET_VM()->next_shape_id);
+ while (cursor < end) {
+ callback(cursor, data);
+ cursor += 1;
+ }
+}
+
+rb_shape_t*
+rb_shape_get_shape_by_id(shape_id_t shape_id)
+{
+ RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
+
+ rb_vm_t *vm = GET_VM();
+ rb_shape_t *shape = &vm->shape_list[shape_id];
+ return shape;
+}
+
+rb_shape_t*
+rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id)
+{
+ RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
+
+ rb_vm_t *vm = GET_VM();
+ rb_shape_t *shape = &vm->shape_list[shape_id];
+ return shape;
+}
+
+rb_shape_t *
+rb_shape_get_parent(rb_shape_t * shape)
+{
+ return rb_shape_get_shape_by_id(shape->parent_id);
+}
+
+#if !SHAPE_IN_BASIC_FLAGS
+shape_id_t
+rb_rclass_shape_id(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ return RCLASS_EXT(obj)->shape_id;
+}
+
+shape_id_t rb_generic_shape_id(VALUE obj);
+#endif
+
+shape_id_t
+rb_shape_get_shape_id(VALUE obj)
+{
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return SPECIAL_CONST_SHAPE_ID;
+ }
+
+#if SHAPE_IN_BASIC_FLAGS
+ return RBASIC_SHAPE_ID(obj);
+#else
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ return ROBJECT_SHAPE_ID(obj);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ return RCLASS_SHAPE_ID(obj);
+ default:
+ return rb_generic_shape_id(obj);
+ }
+#endif
+}
+
+size_t
+rb_shape_depth(rb_shape_t * shape)
+{
+ size_t depth = 1;
+
+ while (shape->parent_id != INVALID_SHAPE_ID) {
+ depth++;
+ shape = rb_shape_get_parent(shape);
+ }
+
+ return depth;
+}
+
+rb_shape_t*
+rb_shape_get_shape(VALUE obj)
+{
+ return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj));
+}
+
+static rb_shape_t*
+get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_shapes_allowed)
+{
+ rb_shape_t *res = NULL;
+
+ // There should never be outgoing edges from "too complex"
+ RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ *variation_created = false;
+
+ if (new_shapes_allowed) {
+ RB_VM_LOCK_ENTER();
+ {
+ bool had_edges = !!shape->edges;
+
+ if (!shape->edges) {
+ shape->edges = rb_id_table_create(0);
+ }
+
+ // Lookup the shape in edges - if there's already an edge and a corresponding shape for it,
+ // we can return that. Otherwise, we'll need to get a new shape
+ VALUE lookup_result;
+ if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
+ res = (rb_shape_t *)lookup_result;
+ }
+ else {
+ *variation_created = had_edges;
+
+ rb_shape_t * new_shape = rb_shape_alloc(id, shape);
+
+ new_shape->type = (uint8_t)shape_type;
+ new_shape->capacity = shape->capacity;
+
+ switch (shape_type) {
+ case SHAPE_IVAR:
+ new_shape->next_iv_index = shape->next_iv_index + 1;
+ break;
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_FROZEN:
+ case SHAPE_T_OBJECT:
+ new_shape->next_iv_index = shape->next_iv_index;
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_ROOT:
+ rb_bug("Unreachable");
+ break;
+ }
+
+ rb_id_table_insert(shape->edges, id, (VALUE)new_shape);
+
+ res = new_shape;
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+ }
+ return res;
+}
+
+MJIT_FUNC_EXPORTED int
+rb_shape_frozen_shape_p(rb_shape_t* shape)
+{
+ return SHAPE_FROZEN == (enum shape_type)shape->type;
+}
+
+static void
+move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
+{
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ RCLASS_IVPTR(obj)[to] = RCLASS_IVPTR(obj)[from];
+ break;
+ case T_OBJECT:
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ ROBJECT_IVPTR(obj)[to] = ROBJECT_IVPTR(obj)[from];
+ break;
+ default: {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ ivtbl->ivptr[to] = ivtbl->ivptr[from];
+ break;
+ }
+ }
+}
+
+static rb_shape_t *
+remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
+{
+ if (shape->parent_id == INVALID_SHAPE_ID) {
+ // We've hit the top of the shape tree and couldn't find the
+ // IV we wanted to remove, so return NULL
+ return NULL;
+ }
+ else {
+ if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
+ // We've hit the edge we wanted to remove, return it's _parent_
+ // as the new parent while we go back down the stack.
+ attr_index_t index = shape->next_iv_index - 1;
+
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ *removed = RCLASS_IVPTR(obj)[index];
+ break;
+ case T_OBJECT:
+ *removed = ROBJECT_IVPTR(obj)[index];
+ break;
+ default: {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ *removed = ivtbl->ivptr[index];
+ break;
+ }
+ }
+ return rb_shape_get_parent(shape);
+ }
+ else {
+ // This isn't the IV we want to remove, keep walking up.
+ rb_shape_t * new_parent = remove_shape_recursive(obj, id, rb_shape_get_parent(shape), removed);
+
+ // We found a new parent. Create a child of the new parent that
+ // has the same attributes as this shape.
+ if (new_parent) {
+ bool dont_care;
+ rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
+ new_child->capacity = shape->capacity;
+ if (new_child->type == SHAPE_IVAR) {
+ move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
+ }
+
+ return new_child;
+ }
+ else {
+ // We went all the way to the top of the shape tree and couldn't
+ // find an IV to remove, so return NULL
+ return NULL;
+ }
+ }
+ }
+}
+
+void
+rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed)
+{
+ rb_shape_t * new_shape = remove_shape_recursive(obj, id, shape, removed);
+ if (new_shape) {
+ rb_shape_set_shape(obj, new_shape);
+ }
+}
+
+void
+rb_shape_transition_shape_frozen(VALUE obj)
+{
+ rb_shape_t* shape = rb_shape_get_shape(obj);
+ RUBY_ASSERT(shape);
+ RUBY_ASSERT(RB_OBJ_FROZEN(obj));
+
+ if (rb_shape_frozen_shape_p(shape) || rb_shape_obj_too_complex(obj)) {
+ return;
+ }
+
+ rb_shape_t* next_shape;
+
+ if (shape == rb_shape_get_root_shape()) {
+ rb_shape_set_shape_id(obj, SPECIAL_CONST_SHAPE_ID);
+ return;
+ }
+
+ bool dont_care;
+ next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
+
+ RUBY_ASSERT(next_shape);
+ rb_shape_set_shape(obj, next_shape);
+}
+
+/*
+ * This function is used for assertions where we don't want to increment
+ * max_iv_count
+ */
+rb_shape_t *
+rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
+{
+ RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
+ bool dont_care;
+ return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true);
+}
+
+rb_shape_t *
+rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
+{
+ RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
+
+ bool allow_new_shape = true;
+
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
+ VALUE klass = rb_obj_class(obj);
+ allow_new_shape = RCLASS_EXT(klass)->variation_count < SHAPE_MAX_VARIATIONS;
+ }
+
+ bool variation_created = false;
+ rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
+
+ if (!new_shape) {
+ RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
+ new_shape = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
+ }
+
+ // Check if we should update max_iv_count on the object's class
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
+ VALUE klass = rb_obj_class(obj);
+ if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
+ RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
+ }
+
+ if (variation_created) {
+ RCLASS_EXT(klass)->variation_count++;
+ }
+ }
+
+ return new_shape;
+}
+
+rb_shape_t *
+rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
+{
+ ID edge_name = rb_make_temporary_id(new_capacity);
+ bool dont_care;
+ rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true);
+ new_shape->capacity = new_capacity;
+ return new_shape;
+}
+
+bool
+rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
+{
+ // It doesn't make sense to ask for the index of an IV that's stored
+ // on an object that is "too complex" as it uses a hash for storing IVs
+ RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ while (shape->parent_id != INVALID_SHAPE_ID) {
+ if (shape->edge_name == id) {
+ enum shape_type shape_type;
+ shape_type = (enum shape_type)shape->type;
+
+ switch (shape_type) {
+ case SHAPE_IVAR:
+ RUBY_ASSERT(shape->next_iv_index > 0);
+ *value = shape->next_iv_index - 1;
+ return true;
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_ROOT:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ return false;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ case SHAPE_FROZEN:
+ rb_bug("Ivar should not exist on transition\n");
+ }
+ }
+ shape = rb_shape_get_parent(shape);
+ }
+ return false;
+}
+
+static rb_shape_t *
+shape_alloc(void)
+{
+ rb_vm_t *vm = GET_VM();
+ shape_id_t shape_id = vm->next_shape_id;
+ vm->next_shape_id++;
+
+ if (shape_id == MAX_SHAPE_ID) {
+ // TODO: Make an OutOfShapesError ??
+ rb_bug("Out of shapes\n");
+ }
+
+ return &GET_VM()->shape_list[shape_id];
+}
+
+rb_shape_t *
+rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id)
+{
+ rb_shape_t * shape = shape_alloc();
+
+ shape->edge_name = edge_name;
+ shape->next_iv_index = 0;
+ shape->parent_id = parent_id;
+
+ return shape;
+}
+
+rb_shape_t *
+rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index)
+{
+ rb_shape_t * shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent));
+ shape->size_pool_index = size_pool_index;
+ return shape;
+}
+
+
+rb_shape_t *
+rb_shape_alloc(ID edge_name, rb_shape_t * parent)
+{
+ return rb_shape_alloc_with_size_pool_index(edge_name, parent, parent->size_pool_index);
+}
+
+MJIT_FUNC_EXPORTED void
+rb_shape_set_shape(VALUE obj, rb_shape_t* shape)
+{
+ rb_shape_set_shape_id(obj, rb_shape_id(shape));
+}
+
+int32_t
+rb_shape_id_offset(void)
+{
+ return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
+}
+
+rb_shape_t *
+rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
+{
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+ rb_shape_t *next_shape = initial_shape;
+
+ if (dest_shape->type != initial_shape->type) {
+ next_shape = rb_shape_traverse_from_new_root(initial_shape, rb_shape_get_parent(dest_shape));
+ if (!next_shape) {
+ return NULL;
+ }
+ }
+
+ switch ((enum shape_type)dest_shape->type) {
+ case SHAPE_IVAR:
+ case SHAPE_FROZEN:
+ if (!next_shape->edges) {
+ return NULL;
+ }
+
+ VALUE lookup_result;
+ if (rb_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
+ next_shape = (rb_shape_t *)lookup_result;
+ }
+ else {
+ return NULL;
+ }
+ break;
+ case SHAPE_ROOT:
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ rb_bug("Unreachable\n");
+ break;
+ }
+
+ return next_shape;
+}
+
+rb_shape_t *
+rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
+{
+ rb_shape_t * midway_shape;
+
+ RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
+
+ if (dest_shape->type != initial_shape->type) {
+ midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
+ }
+ else {
+ midway_shape = initial_shape;
+ }
+
+ switch ((enum shape_type)dest_shape->type) {
+ case SHAPE_IVAR:
+ if (midway_shape->capacity <= midway_shape->next_iv_index) {
+ // There isn't enough room to write this IV, so we need to increase the capacity
+ midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2);
+ }
+
+ midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
+ break;
+ case SHAPE_ROOT:
+ case SHAPE_FROZEN:
+ case SHAPE_CAPACITY_CHANGE:
+ case SHAPE_INITIAL_CAPACITY:
+ case SHAPE_T_OBJECT:
+ break;
+ case SHAPE_OBJ_TOO_COMPLEX:
+ rb_bug("Unreachable\n");
+ break;
+ }
+
+ return midway_shape;
+}
+
+bool
+rb_shape_obj_too_complex(VALUE obj)
+{
+ return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
+}
+
+void
+rb_shape_set_too_complex(VALUE obj)
+{
+ RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+}
+
+size_t
+rb_shape_edges_count(rb_shape_t *shape)
+{
+ if (shape->edges) {
+ return rb_id_table_size(shape->edges);
+ }
+ return 0;
+}
+
+size_t
+rb_shape_memsize(rb_shape_t *shape)
+{
+ size_t memsize = sizeof(rb_shape_t);
+ if (shape->edges) {
+ memsize += rb_id_table_memsize(shape->edges);
+ }
+ return memsize;
+}
+
+#if SHAPE_DEBUG
+/*
+ * Exposing Shape to Ruby via RubyVM.debug_shape
+ */
+
+/* :nodoc: */
+static VALUE
+rb_shape_too_complex(VALUE self)
+{
+ rb_shape_t * shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ if (rb_shape_id(shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+}
+
+static VALUE
+parse_key(ID key)
+{
+ if (is_instance_id(key)) {
+ return ID2SYM(key);
+ }
+ return LONG2NUM(key);
+}
+
+static VALUE rb_shape_edge_name(rb_shape_t * shape);
+
+static VALUE
+rb_shape_t_to_rb_cShape(rb_shape_t *shape)
+{
+ VALUE rb_cShape = rb_const_get(rb_cRubyVM, rb_intern("Shape"));
+
+ VALUE obj = rb_struct_new(rb_cShape,
+ INT2NUM(rb_shape_id(shape)),
+ INT2NUM(shape->parent_id),
+ rb_shape_edge_name(shape),
+ INT2NUM(shape->next_iv_index),
+ INT2NUM(shape->size_pool_index),
+ INT2NUM(shape->type),
+ INT2NUM(shape->capacity));
+ rb_obj_freeze(obj);
+ return obj;
+}
+
+static enum rb_id_table_iterator_result
+rb_edges_to_hash(ID key, VALUE value, void *ref)
+{
+ rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value));
+ return ID_TABLE_CONTINUE;
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_edges(VALUE self)
+{
+ rb_shape_t* shape;
+
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+
+ VALUE hash = rb_hash_new();
+
+ if (shape->edges) {
+ rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash);
+ }
+
+ return hash;
+}
+
+static VALUE
+rb_shape_edge_name(rb_shape_t * shape)
+{
+ if (shape->edge_name) {
+ if (is_instance_id(shape->edge_name)) {
+ return ID2SYM(shape->edge_name);
+ }
+ return INT2NUM(shape->capacity);
+ }
+ return Qnil;
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_export_depth(VALUE self)
+{
+ rb_shape_t* shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ return SIZET2NUM(rb_shape_depth(shape));
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_parent(VALUE self)
+{
+ rb_shape_t * shape;
+ shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
+ if (shape->parent_id != INVALID_SHAPE_ID) {
+ return rb_shape_t_to_rb_cShape(rb_shape_get_parent(shape));
+ }
+ else {
+ return Qnil;
+ }
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_debug_shape(VALUE self, VALUE obj)
+{
+ return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj));
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_root_shape(VALUE self)
+{
+ return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
+}
+
+VALUE rb_obj_shape(rb_shape_t* shape);
+
+static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
+{
+ rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value));
+ return ID_TABLE_CONTINUE;
+}
+
+static VALUE edges(struct rb_id_table* edges)
+{
+ VALUE hash = rb_hash_new();
+ if (edges)
+ rb_id_table_foreach(edges, collect_keys_and_values, &hash);
+ return hash;
+}
+
+/* :nodoc: */
+VALUE
+rb_obj_shape(rb_shape_t* shape)
+{
+ VALUE rb_shape = rb_hash_new();
+
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape)));
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges));
+
+ if (shape == rb_shape_get_root_shape()) {
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID));
+ }
+ else {
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(shape->parent_id));
+ }
+
+ rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name));
+ return rb_shape;
+}
+
+/* :nodoc: */
+static VALUE
+shape_transition_tree(VALUE self)
+{
+ return rb_obj_shape(rb_shape_get_root_shape());
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_find_by_id(VALUE mod, VALUE id)
+{
+ shape_id_t shape_id = NUM2UINT(id);
+ if (shape_id >= GET_VM()->next_shape_id) {
+ rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id);
+ }
+ return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id));
+}
+#endif
+
+void
+Init_default_shapes(void)
+{
+ id_frozen = rb_make_internal_id();
+ id_t_object = rb_make_internal_id();
+
+ // Shapes by size pool
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ size_pool_edge_names[i] = rb_make_internal_id();
+ }
+
+ // Root shape
+ rb_shape_t * root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
+ root->capacity = (uint32_t)((rb_size_pool_slot_size(0) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ root->type = SHAPE_ROOT;
+ root->size_pool_index = 0;
+ GET_VM()->root_shape = root;
+ RUBY_ASSERT(rb_shape_id(GET_VM()->root_shape) == ROOT_SHAPE_ID);
+
+ // Shapes by size pool
+ for (int i = 1; i < SIZE_POOL_COUNT; i++) {
+ uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ rb_shape_t * new_shape = rb_shape_transition_shape_capa(root, capa);
+ new_shape->type = SHAPE_INITIAL_CAPACITY;
+ new_shape->size_pool_index = i;
+ RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i);
+ }
+
+ // Make shapes for T_OBJECT
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_shape_t * shape = rb_shape_get_shape_by_id(i);
+ bool dont_care;
+ rb_shape_t * t_object_shape =
+ get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT, &dont_care, true);
+ t_object_shape->edges = rb_id_table_create(0);
+ RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
+ }
+
+ bool dont_care;
+ // Special const shape
+#if RUBY_DEBUG
+ rb_shape_t * special_const_shape =
+#endif
+ get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
+ RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
+ RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1));
+ RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
+
+ rb_shape_t * hash_fallback_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
+ hash_fallback_shape->type = SHAPE_OBJ_TOO_COMPLEX;
+ hash_fallback_shape->size_pool_index = 0;
+ RUBY_ASSERT(OBJ_TOO_COMPLEX_SHAPE_ID == (GET_VM()->next_shape_id - 1));
+ RUBY_ASSERT(rb_shape_id(hash_fallback_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
+}
+
+void
+Init_shape(void)
+{
+#if SHAPE_DEBUG
+ VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
+ "id",
+ "parent_id",
+ "edge_name",
+ "next_iv_index",
+ "size_pool_index",
+ "type",
+ "capacity",
+ NULL);
+
+ rb_define_method(rb_cShape, "parent", rb_shape_parent, 0);
+ rb_define_method(rb_cShape, "edges", rb_shape_edges, 0);
+ rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0);
+ rb_define_method(rb_cShape, "too_complex?", rb_shape_too_complex, 0);
+ rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
+ rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
+ rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
+ rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN));
+ rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS));
+ rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
+ rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
+ rb_define_const(rb_cShape, "OBJ_TOO_COMPLEX_SHAPE_ID", INT2NUM(OBJ_TOO_COMPLEX_SHAPE_ID));
+ rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
+
+ rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0);
+ rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
+ rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
+ rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
+#endif
+}
diff --git a/shape.h b/shape.h
new file mode 100644
index 0000000000..8f3451d492
--- /dev/null
+++ b/shape.h
@@ -0,0 +1,232 @@
+#ifndef RUBY_SHAPE_H
+#define RUBY_SHAPE_H
+
+#include "internal/gc.h"
+
+#if (SIZEOF_UINT64_T == SIZEOF_VALUE)
+#define SIZEOF_SHAPE_T 4
+#define SHAPE_IN_BASIC_FLAGS 1
+typedef uint32_t attr_index_t;
+#else
+#define SIZEOF_SHAPE_T 2
+#define SHAPE_IN_BASIC_FLAGS 0
+typedef uint16_t attr_index_t;
+#endif
+
+#define MAX_IVARS (attr_index_t)(-1)
+
+#if SIZEOF_SHAPE_T == 4
+typedef uint32_t shape_id_t;
+# define SHAPE_ID_NUM_BITS 32
+#else
+typedef uint16_t shape_id_t;
+# define SHAPE_ID_NUM_BITS 16
+#endif
+
+# define SHAPE_MASK (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1)
+# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
+
+# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS)
+
+# define SHAPE_BITMAP_SIZE 16384
+
+# define SHAPE_MAX_VARIATIONS 8
+
+# define MAX_SHAPE_ID (SHAPE_MASK - 1)
+# define INVALID_SHAPE_ID SHAPE_MASK
+# define ROOT_SHAPE_ID 0x0
+
+# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2)
+# define OBJ_TOO_COMPLEX_SHAPE_ID (SPECIAL_CONST_SHAPE_ID + 1)
+
+struct rb_shape {
+ struct rb_id_table * edges; // id_table from ID (ivar) to next shape
+ ID edge_name; // ID (ivar) for transition from parent to rb_shape
+ attr_index_t next_iv_index;
+ uint32_t capacity; // Total capacity of the object with this shape
+ uint8_t type;
+ uint8_t size_pool_index;
+ shape_id_t parent_id;
+};
+
+typedef struct rb_shape rb_shape_t;
+
+enum shape_type {
+ SHAPE_ROOT,
+ SHAPE_IVAR,
+ SHAPE_FROZEN,
+ SHAPE_CAPACITY_CHANGE,
+ SHAPE_INITIAL_CAPACITY,
+ SHAPE_T_OBJECT,
+ SHAPE_OBJ_TOO_COMPLEX,
+};
+
+#if SHAPE_IN_BASIC_FLAGS
+static inline shape_id_t
+RBASIC_SHAPE_ID(VALUE obj)
+{
+ RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
+ return (shape_id_t)(SHAPE_MASK & ((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT));
+}
+
+static inline void
+RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ // Ractors are occupying the upper 32 bits of flags, but only in debug mode
+ // Object shapes are occupying top bits
+ RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
+ RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
+}
+
+static inline shape_id_t
+ROBJECT_SHAPE_ID(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ return RBASIC_SHAPE_ID(obj);
+}
+
+static inline void
+ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RBASIC_SET_SHAPE_ID(obj, shape_id);
+}
+
+static inline shape_id_t
+RCLASS_SHAPE_ID(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ return RBASIC_SHAPE_ID(obj);
+}
+
+#else
+
+static inline shape_id_t
+ROBJECT_SHAPE_ID(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ return (shape_id_t)(SHAPE_MASK & (RBASIC(obj)->flags >> SHAPE_FLAG_SHIFT));
+}
+
+static inline void
+ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
+{
+ RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
+ RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
+}
+
+MJIT_SYMBOL_EXPORT_BEGIN
+shape_id_t rb_rclass_shape_id(VALUE obj);
+MJIT_SYMBOL_EXPORT_END
+
+static inline shape_id_t RCLASS_SHAPE_ID(VALUE obj)
+{
+ return rb_rclass_shape_id(obj);
+}
+
+#endif
+
+bool rb_shape_root_shape_p(rb_shape_t* shape);
+rb_shape_t * rb_shape_get_root_shape(void);
+uint8_t rb_shape_id_num_bits(void);
+int32_t rb_shape_id_offset(void);
+
+rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id);
+rb_shape_t * rb_shape_get_parent(rb_shape_t * shape);
+
+MJIT_SYMBOL_EXPORT_BEGIN
+rb_shape_t* rb_shape_get_shape_by_id(shape_id_t shape_id);
+shape_id_t rb_shape_get_shape_id(VALUE obj);
+rb_shape_t * rb_shape_get_next_iv_shape(rb_shape_t * shape, ID id);
+bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value);
+bool rb_shape_obj_too_complex(VALUE obj);
+MJIT_SYMBOL_EXPORT_END
+
+void rb_shape_set_shape(VALUE obj, rb_shape_t* shape);
+rb_shape_t* rb_shape_get_shape(VALUE obj);
+int rb_shape_frozen_shape_p(rb_shape_t* shape);
+void rb_shape_transition_shape_frozen(VALUE obj);
+void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed);
+rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t * shape, uint32_t new_capacity);
+rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id);
+
+rb_shape_t * rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape);
+
+static inline uint32_t
+ROBJECT_IV_CAPACITY(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ // Asking for capacity doesn't make sense when the object is using
+ // a hash table for storing instance variables
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity;
+}
+
+static inline st_table *
+ROBJECT_IV_HASH(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ return (st_table *)ROBJECT(obj)->as.heap.ivptr;
+}
+
+static inline void
+ROBJECT_SET_IV_HASH(VALUE obj, const struct rb_id_table *tbl)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ ROBJECT(obj)->as.heap.ivptr = (VALUE *)tbl;
+}
+
+size_t rb_id_table_size(const struct rb_id_table *tbl);
+
+static inline uint32_t
+ROBJECT_IV_COUNT(VALUE obj)
+{
+ if (ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj));
+ }
+ else {
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+ RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->next_iv_index;
+ }
+}
+
+static inline uint32_t
+RBASIC_IV_COUNT(VALUE obj)
+{
+ return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj))->next_iv_index;
+}
+
+static inline uint32_t
+RCLASS_IV_COUNT(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ uint32_t ivc = rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))->next_iv_index;
+ return ivc;
+}
+
+rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent);
+rb_shape_t * rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index);
+rb_shape_t * rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id);
+
+rb_shape_t *rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *orig_shape);
+
+bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
+
+VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
+VALUE rb_shape_flags_mask(void);
+void rb_shape_set_too_complex(VALUE obj);
+
+// For ext/objspace
+RUBY_SYMBOL_EXPORT_BEGIN
+typedef void each_shape_callback(rb_shape_t * shape, void *data);
+void rb_shape_each_shape(each_shape_callback callback, void *data);
+size_t rb_shape_memsize(rb_shape_t *shape);
+size_t rb_shape_edges_count(rb_shape_t *shape);
+size_t rb_shape_depth(rb_shape_t *shape);
+shape_id_t rb_shape_id(rb_shape_t * shape);
+RUBY_SYMBOL_EXPORT_END
+
+#endif
diff --git a/signal.c b/signal.c
index 874afd28d5..1072cb47f1 100644
--- a/signal.c
+++ b/signal.c
@@ -44,6 +44,7 @@
#include "ruby_atomic.h"
#include "vm_core.h"
#include "ractor_core.h"
+#include "ruby/internal/attr/nonstring.h"
#ifdef NEED_RUBY_ATOMIC_OPS
rb_atomic_t
@@ -1005,7 +1006,7 @@ check_reserved_signal_(const char *name, size_t name_len)
if (prev) {
ssize_t RB_UNUSED_VAR(err);
-#define NOZ(name, str) name[sizeof(str)-1] = str
+#define NOZ(name, str) RBIMPL_ATTR_NONSTRING() name[sizeof(str)-1] = str
static const char NOZ(msg1, " received in ");
static const char NOZ(msg2, " handler\n");
@@ -1129,7 +1130,7 @@ rb_signal_exec(rb_thread_t *th, int sig)
break;
}
}
- else if (cmd == Qundef) {
+ else if (UNDEF_P(cmd)) {
rb_threadptr_signal_exit(th);
}
else {
diff --git a/siphash.c b/siphash.c
index 294e75eaec..62de622778 100644
--- a/siphash.c
+++ b/siphash.c
@@ -34,10 +34,11 @@
#error "Only strictly little or big endian supported"
#endif
+/* __POWERPC__ added to accommodate Darwin case. */
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
- defined(__powerpc64__) || defined(__aarch64__) || \
+ defined(__powerpc64__) || defined(__POWERPC__) || defined(__aarch64__) || \
defined(__mc68020__)
# define UNALIGNED_WORD_ACCESS 1
# endif
@@ -139,6 +140,9 @@ xor64_to(uint64_t *v, const uint64_t s)
#endif
static const union {
+#if defined(__has_attribute) && __has_attribute(nonstring)
+ __attribute__((nonstring))
+#endif
char bin[32];
uint64_t u64[4];
} sip_init_state_bin = {"uespemos""modnarod""arenegyl""setybdet"};
diff --git a/spec/README.md b/spec/README.md
index 6b82f8f06a..4fcf090759 100644
--- a/spec/README.md
+++ b/spec/README.md
@@ -10,6 +10,14 @@ To run rspec for bundler:
make test-bundler
```
+or run rspec with parallel execution:
+
+```bash
+make test-bundler-parallel
+```
+
+If you specify `BUNDLER_SPECS=foo/bar_spec.rb` then only `spec/bundler/foo/bar_spec.rb` will be run.
+
# spec/ruby
ruby/spec (https://github.com/ruby/spec/) is
@@ -138,3 +146,15 @@ end
```
For more details, see `spec/ruby/CONTRIBUTING.md`.
+
+# spec/syntax_suggest
+
+## Running spec/syntax_suggest
+
+To run rspec for syntax_suggest:
+
+```bash
+make test-syntax-suggest
+```
+
+If you specify `SYNTAX_SUGGEST_SPECS=foo/bar_spec.rb` then only `spec/syntax_suggest/foo/bar_spec.rb` will be run.
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index aeadcf9720..54fedc8568 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -4,6 +4,71 @@ require "bundler"
require "tmpdir"
RSpec.describe Bundler do
+ describe "#load_marshal" do
+ it "is a private method and raises an error" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method `load_marshal' called/)
+ end
+
+ it "loads any data" do
+ data = Marshal.dump(Bundler)
+ expect(Bundler.send(:load_marshal, data)).to eq(Bundler)
+ end
+ end
+
+ describe "#safe_load_marshal" do
+ it "fails on unexpected class" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.safe_load_marshal(data) }.to raise_error(Bundler::MarshalError)
+ end
+
+ it "loads simple structure" do
+ simple_structure = { "name" => [:abc] }
+ data = Marshal.dump(simple_structure)
+ expect(Bundler.safe_load_marshal(data)).to eq(simple_structure)
+ end
+
+ it "loads Gem::Specification" do
+ gem_spec = Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = Gem::Version.new("2.4.7")
+ s.installed_by_version = Gem::Version.new("0")
+ s.authors = ["André Arko",
+ "Samuel Giddins",
+ "Colby Swandale",
+ "Hiroshi Shibata",
+ "David Rodríguez",
+ "Grey Baker",
+ "Stephanie Morillo",
+ "Chris Morris",
+ "James Wen",
+ "Tim Moore",
+ "André Medeiros",
+ "Jessica Lynn Suttles",
+ "Terence Lee",
+ "Carl Lerche",
+ "Yehuda Katz"]
+ s.date = Time.utc(2023, 2, 15)
+ s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
+ s.email = ["team@bundler.io"]
+ s.homepage = "https://bundler.io"
+ s.metadata = { "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler" }
+ s.require_paths = ["lib"]
+ s.required_ruby_version = Gem::Requirement.new([">= 2.6.0"])
+ s.required_rubygems_version = Gem::Requirement.new([">= 3.0.1"])
+ s.rubygems_version = "3.4.7"
+ s.specification_version = 4
+ s.summary = "The best way to manage your application's dependencies"
+ s.license = false
+ end
+ data = Marshal.dump(gem_spec)
+ expect(Bundler.safe_load_marshal(data)).to eq(gem_spec)
+ end
+ end
+
describe "#load_gemspec_uncached" do
let(:app_gemspec_path) { tmp("test.gemspec") }
subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
@@ -167,9 +232,9 @@ RSpec.describe Bundler do
allow(::Bundler::FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
allow(File).to receive(:world_writable?).and_return(true)
message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
+It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
+Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
EOF
expect(bundler_ui).to receive(:warn).with(message)
expect { Bundler.send(:rm_rf, bundled_app) }.to raise_error(Bundler::PathError)
@@ -189,22 +254,6 @@ EOF
Bundler.mkdir_p(bundled_app.join("foo", "bar"))
expect(bundled_app.join("foo", "bar")).to exist
end
-
- context "when mkdir_p requires sudo" do
- it "creates a new folder using sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to receive(:sudo).and_return true
- Bundler.mkdir_p(bundled_app.join("foo"))
- end
- end
-
- context "with :no_sudo option" do
- it "forces mkdir_p to not use sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to_not receive(:sudo)
- Bundler.mkdir_p(bundled_app.join("foo"), :no_sudo => true)
- end
- end
end
describe "#user_home" do
@@ -268,118 +317,6 @@ EOF
end
end
- describe "#requires_sudo?" do
- let!(:tmpdir) { Dir.mktmpdir }
- let(:bundle_path) { Pathname("#{tmpdir}/bundle") }
-
- def clear_cached_requires_sudo
- return unless Bundler.instance_variable_defined?(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo)
- end
-
- before do
- clear_cached_requires_sudo
- allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
- allow(Bundler).to receive(:bundle_path).and_return(bundle_path)
- end
-
- after do
- FileUtils.rm_rf(tmpdir)
- clear_cached_requires_sudo
- end
-
- subject { Bundler.requires_sudo? }
-
- context "bundle_path doesn't exist" do
- it { should be false }
-
- context "and parent dir can't be written" do
- before do
- FileUtils.chmod(0o500, tmpdir)
- end
-
- it { should be true }
- end
-
- context "with unwritable files in a parent dir" do
- # Regression test for https://github.com/rubygems/bundler/pull/6316
- # It doesn't matter if there are other unwritable files so long as
- # bundle_path can be created
- before do
- file = File.join(tmpdir, "unrelated_file")
- FileUtils.touch(file)
- FileUtils.chmod(0o400, file)
- end
-
- it { should be false }
- end
- end
-
- context "bundle_path exists" do
- before do
- FileUtils.mkdir_p(bundle_path)
- end
-
- it { should be false }
-
- context "and is unwritable" do
- before do
- FileUtils.chmod(0o500, bundle_path)
- end
-
- it { should be true }
- end
- end
-
- context "path writability" do
- before do
- FileUtils.mkdir_p("tmp/vendor/bundle")
- FileUtils.mkdir_p("tmp/vendor/bin_dir")
- end
- after do
- FileUtils.rm_rf("tmp/vendor/bundle")
- FileUtils.rm_rf("tmp/vendor/bin_dir")
- end
- context "writable paths" do
- it "should return false and display nothing" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- expect(Bundler.ui).to_not receive(:warn)
- expect(Bundler.requires_sudo?).to eq(false)
- end
- end
- context "unwritable paths" do
- before do
- FileUtils.touch("tmp/vendor/bundle/unwritable1.txt")
- FileUtils.touch("tmp/vendor/bundle/unwritable2.txt")
- FileUtils.touch("tmp/vendor/bin_dir/unwritable3.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable1.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable2.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bin_dir/unwritable3.txt")
- end
- it "should return true and display warn message" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- bin_dir = Pathname("tmp/vendor/bin_dir/")
-
- # allow File#writable? to be called with args other than the stubbed on below
- allow(File).to receive(:writable?).and_call_original
-
- # fake make the directory unwritable
- allow(File).to receive(:writable?).with(bin_dir).and_return(false)
- allow(Bundler).to receive(:system_bindir).and_return(Pathname("tmp/vendor/bin_dir/"))
- message = <<-MESSAGE.chomp
-Following files may not be writable, so sudo is needed:
- tmp/vendor/bin_dir/
- tmp/vendor/bundle/unwritable1.txt
- tmp/vendor/bundle/unwritable2.txt
-MESSAGE
- expect(Bundler.ui).to receive(:warn).with(message)
- expect(Bundler.requires_sudo?).to eq(true)
- end
- end
- end
- end
-
context "user cache dir" do
let(:home_path) { Pathname.new(ENV["HOME"]) }
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index 28ade90138..b752cd7e70 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -129,6 +129,52 @@ RSpec.describe "bundle executable" do
end
end
+ describe "bundle outdated" do
+ let(:run_command) do
+ bundle "install"
+
+ bundle "outdated #{flags}", :raise_on_error => false
+ end
+
+ before do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", '0.9.1'
+ G
+ end
+
+ context "with --groups flag" do
+ let(:flags) { "--groups" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("Gem Current Latest Requested Groups")
+ expect(out).to include("rack 0.9.1 1.0.0 = 0.9.1 default")
+ end
+ end
+
+ context "with --parseable" do
+ let(:flags) { "--parseable" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("rack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
+ end
+
+ context "with --groups and --parseable" do
+ let(:flags) { "--groups --parseable" }
+
+ it "prints a simplified message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("rack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
+ end
+ end
+
describe "printing the outdated warning" do
shared_examples_for "no warning" do
it "prints no warning" do
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index 13688c2b3d..59b958ae42 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -82,6 +82,24 @@ RSpec.describe Bundler::Definition do
G
end
+ it "with an explicit update" do
+ build_repo4 do
+ build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
+ build_gem("ffi", "1.9.23")
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "ffi"
+ G
+
+ bundle "lock --add-platform java"
+
+ bundle "update ffi", :env => { "DEBUG" => "1" }
+
+ expect(out).to match(/because bundler is unlocking gems: \(ffi\)/)
+ end
+
it "for a path gem with deps and no changes" do
build_lib "foo", "1.0", :path => lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
@@ -136,7 +154,7 @@ RSpec.describe Bundler::Definition do
only_java (1.1-java)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
only_java
@@ -175,31 +193,6 @@ RSpec.describe Bundler::Definition do
describe "initialize" do
context "gem version promoter" do
- context "with lockfile" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "foo"
- G
-
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
-
- it "should get a locked specs list when updating all" do
- definition = Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, true)
- locked_specs = definition.gem_version_promoter.locked_specs
- expect(locked_specs.to_a.map(&:name)).to eq ["foo"]
- expect(definition.instance_variable_get("@locked_specs").empty?).to eq true
- end
- end
-
- context "without gemfile or lockfile" do
- it "should not attempt to parse empty lockfile contents" do
- definition = Bundler::Definition.new(nil, [], mock_source_list, true)
- expect(definition.gem_version_promoter.locked_specs.to_a).to eq []
- end
- end
-
context "eager unlock" do
let(:source_list) do
Bundler::SourceList.new.tap do |source_list|
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
deleted file mode 100644
index 8d02a33725..0000000000
--- a/spec/bundler/bundler/dep_proxy_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe Bundler::DepProxy do
- let(:dep) { Bundler::Dependency.new("rake", ">= 0") }
- subject { described_class.get_proxy(dep, Gem::Platform::RUBY) }
- let(:same) { subject }
- let(:other) { described_class.get_proxy(dep, Gem::Platform::RUBY) }
- let(:different) { described_class.get_proxy(dep, Gem::Platform::JAVA) }
-
- describe "#eql?" do
- it { expect(subject.eql?(same)).to be true }
- it { expect(subject.eql?(other)).to be true }
- it { expect(subject.eql?(different)).to be false }
- it { expect(subject.eql?(nil)).to be false }
- it { expect(subject.eql?("foobar")).to be false }
- end
-
- describe "must use factory methods" do
- it { expect { described_class.new(dep, Gem::Platform::RUBY) }.to raise_error NoMethodError }
- it { expect { subject.dup }.to raise_error NoMethodError }
- it { expect { subject.clone }.to raise_error NoMethodError }
- end
-
- describe "frozen" do
- if Gem.ruby_version >= Gem::Version.new("2.5.0")
- error = Object.const_get("FrozenError")
- else
- error = RuntimeError
- end
- it { expect { subject.instance_variable_set(:@__platform, {}) }.to raise_error error }
- end
-end
diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb
new file mode 100644
index 0000000000..6e346c36c1
--- /dev/null
+++ b/spec/bundler/bundler/dependency_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Dependency do
+ let(:options) do
+ {}
+ end
+ let(:dependency) do
+ described_class.new(
+ "test_gem",
+ "1.0.0",
+ options
+ )
+ end
+
+ describe "to_lock" do
+ it "returns formatted string" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)")
+ end
+
+ it "matches format of Gem::Dependency#to_lock" do
+ gem_dependency = Gem::Dependency.new("test_gem", "1.0.0")
+ expect(dependency.to_lock).to eq(gem_dependency.to_lock)
+ end
+
+ context "when source is passed" do
+ let(:options) do
+ {
+ "source" => Bundler::Source::Git.new({}),
+ }
+ end
+
+ it "returns formatted string with exclamation mark" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)!")
+ end
+ end
+ end
+
+ describe "PLATFORM_MAP" do
+ subject { described_class::PLATFORM_MAP }
+
+ # rubocop:disable Naming/VariableNumber
+ let(:platforms) do
+ { :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :ruby_22 => Gem::Platform::RUBY,
+ :ruby_23 => Gem::Platform::RUBY,
+ :ruby_24 => Gem::Platform::RUBY,
+ :ruby_25 => Gem::Platform::RUBY,
+ :ruby_26 => Gem::Platform::RUBY,
+ :ruby_27 => Gem::Platform::RUBY,
+ :ruby_30 => Gem::Platform::RUBY,
+ :ruby_31 => Gem::Platform::RUBY,
+ :ruby_32 => Gem::Platform::RUBY,
+ :ruby_33 => Gem::Platform::RUBY,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mri_22 => Gem::Platform::RUBY,
+ :mri_23 => Gem::Platform::RUBY,
+ :mri_24 => Gem::Platform::RUBY,
+ :mri_25 => Gem::Platform::RUBY,
+ :mri_26 => Gem::Platform::RUBY,
+ :mri_27 => Gem::Platform::RUBY,
+ :mri_30 => Gem::Platform::RUBY,
+ :mri_31 => Gem::Platform::RUBY,
+ :mri_32 => Gem::Platform::RUBY,
+ :mri_33 => Gem::Platform::RUBY,
+ :rbx => Gem::Platform::RUBY,
+ :truffleruby => Gem::Platform::RUBY,
+ :jruby => Gem::Platform::JAVA,
+ :jruby_18 => Gem::Platform::JAVA,
+ :jruby_19 => Gem::Platform::JAVA,
+ :windows => Gem::Platform::WINDOWS,
+ :windows_18 => Gem::Platform::WINDOWS,
+ :windows_19 => Gem::Platform::WINDOWS,
+ :windows_20 => Gem::Platform::WINDOWS,
+ :windows_21 => Gem::Platform::WINDOWS,
+ :windows_22 => Gem::Platform::WINDOWS,
+ :windows_23 => Gem::Platform::WINDOWS,
+ :windows_24 => Gem::Platform::WINDOWS,
+ :windows_25 => Gem::Platform::WINDOWS,
+ :windows_26 => Gem::Platform::WINDOWS,
+ :windows_27 => Gem::Platform::WINDOWS,
+ :windows_30 => Gem::Platform::WINDOWS,
+ :windows_31 => Gem::Platform::WINDOWS,
+ :windows_32 => Gem::Platform::WINDOWS,
+ :windows_33 => Gem::Platform::WINDOWS,
+ :mswin => Gem::Platform::MSWIN,
+ :mswin_18 => Gem::Platform::MSWIN,
+ :mswin_19 => Gem::Platform::MSWIN,
+ :mswin_20 => Gem::Platform::MSWIN,
+ :mswin_21 => Gem::Platform::MSWIN,
+ :mswin_22 => Gem::Platform::MSWIN,
+ :mswin_23 => Gem::Platform::MSWIN,
+ :mswin_24 => Gem::Platform::MSWIN,
+ :mswin_25 => Gem::Platform::MSWIN,
+ :mswin_26 => Gem::Platform::MSWIN,
+ :mswin_27 => Gem::Platform::MSWIN,
+ :mswin_30 => Gem::Platform::MSWIN,
+ :mswin_31 => Gem::Platform::MSWIN,
+ :mswin_32 => Gem::Platform::MSWIN,
+ :mswin_33 => Gem::Platform::MSWIN,
+ :mswin64 => Gem::Platform::MSWIN64,
+ :mswin64_19 => Gem::Platform::MSWIN64,
+ :mswin64_20 => Gem::Platform::MSWIN64,
+ :mswin64_21 => Gem::Platform::MSWIN64,
+ :mswin64_22 => Gem::Platform::MSWIN64,
+ :mswin64_23 => Gem::Platform::MSWIN64,
+ :mswin64_24 => Gem::Platform::MSWIN64,
+ :mswin64_25 => Gem::Platform::MSWIN64,
+ :mswin64_26 => Gem::Platform::MSWIN64,
+ :mswin64_27 => Gem::Platform::MSWIN64,
+ :mswin64_30 => Gem::Platform::MSWIN64,
+ :mswin64_31 => Gem::Platform::MSWIN64,
+ :mswin64_32 => Gem::Platform::MSWIN64,
+ :mswin64_33 => Gem::Platform::MSWIN64,
+ :mingw => Gem::Platform::MINGW,
+ :mingw_18 => Gem::Platform::MINGW,
+ :mingw_19 => Gem::Platform::MINGW,
+ :mingw_20 => Gem::Platform::MINGW,
+ :mingw_21 => Gem::Platform::MINGW,
+ :mingw_22 => Gem::Platform::MINGW,
+ :mingw_23 => Gem::Platform::MINGW,
+ :mingw_24 => Gem::Platform::MINGW,
+ :mingw_25 => Gem::Platform::MINGW,
+ :mingw_26 => Gem::Platform::MINGW,
+ :mingw_27 => Gem::Platform::MINGW,
+ :mingw_30 => Gem::Platform::MINGW,
+ :mingw_31 => Gem::Platform::MINGW,
+ :mingw_32 => Gem::Platform::MINGW,
+ :mingw_33 => Gem::Platform::MINGW,
+ :x64_mingw => Gem::Platform::X64_MINGW,
+ :x64_mingw_20 => Gem::Platform::X64_MINGW,
+ :x64_mingw_21 => Gem::Platform::X64_MINGW,
+ :x64_mingw_22 => Gem::Platform::X64_MINGW,
+ :x64_mingw_23 => Gem::Platform::X64_MINGW,
+ :x64_mingw_24 => Gem::Platform::X64_MINGW,
+ :x64_mingw_25 => Gem::Platform::X64_MINGW,
+ :x64_mingw_26 => Gem::Platform::X64_MINGW,
+ :x64_mingw_27 => Gem::Platform::X64_MINGW,
+ :x64_mingw_30 => Gem::Platform::X64_MINGW,
+ :x64_mingw_31 => Gem::Platform::X64_MINGW,
+ :x64_mingw_32 => Gem::Platform::X64_MINGW,
+ :x64_mingw_33 => Gem::Platform::X64_MINGW }
+ end
+ # rubocop:enable Naming/VariableNumber
+
+ it "includes all platforms" do
+ expect(subject).to eq(platforms)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index 19c8f2e889..8b5bf930f2 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Bundler::Dsl do
subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5")
github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
- expect(subject.dependencies.first.source.branch).to eq("refs/pull/5/head")
+ expect(subject.dependencies.first.source.ref).to eq("refs/pull/5/head")
end
it "rejects :github PR URI with a branch, ref or tag" do
@@ -139,14 +139,19 @@ RSpec.describe Bundler::Dsl do
describe "#gem" do
# rubocop:disable Naming/VariableNumber
[:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27,
- :ruby_30, :ruby_31, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :mri_26,
- :mri_27, :mri_30, :mri_31, :jruby, :rbx, :truffleruby].each do |platform|
+ :ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
+ :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
subject.gem("foo", :platform => platform)
end
end
# rubocop:enable Naming/VariableNumber
+ it "allows platforms matching the running Ruby version" do
+ platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
+ subject.gem("foo", :platform => platform)
+ end
+
it "rejects invalid platforms" do
expect { subject.gem("foo", :platform => :bogus) }.
to raise_error(Bundler::GemfileError, /is not a valid platform/)
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
index 02a90d507a..7dd6925007 100644
--- a/spec/bundler/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -48,6 +48,31 @@ RSpec.describe Bundler::EndpointSpecification do
end
end
+ describe "#required_ruby_version" do
+ context "required_ruby_version is already set on endpoint specification" do
+ existing_value = "already set value"
+ let(:required_ruby_version) { existing_value }
+
+ it "should return the current value when already set on endpoint specification" do
+ expect(spec.required_ruby_version). eql?(existing_value)
+ end
+ end
+
+ it "should return the remote spec value when not set on endpoint specification and remote spec has one" do
+ remote_value = "remote_value"
+ remote_spec = double(:remote_spec, :required_ruby_version => remote_value, :required_rubygems_version => nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+
+ expect(spec.required_ruby_version). eql?(remote_value)
+ end
+
+ it "should use the default Gem Requirement value when not set on endpoint specification and not set on remote spec" do
+ remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ expect(spec.required_ruby_version). eql?(Gem::Requirement.default)
+ end
+ end
+
it "supports equality comparison" do
remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index a6f4b2ba85..a00489d0e5 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -4,7 +4,7 @@ require "bundler/settings"
require "openssl"
RSpec.describe Bundler::Env do
- let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
+ let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil) }
describe "#report" do
it "prints the environment" do
@@ -34,8 +34,6 @@ RSpec.describe Bundler::Env do
end
it "prints user home" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
out = described_class.report
expect(out).to include("User Home /a/b/c")
@@ -43,8 +41,6 @@ RSpec.describe Bundler::Env do
end
it "prints user path" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
allow(File).to receive(:exist?)
allow(File).to receive(:exist?).with("/a/b/c/.gem").and_return(true)
@@ -221,7 +217,7 @@ RSpec.describe Bundler::Env do
context "when the git version is OS specific" do
it "includes OS specific information with the version number" do
- expect(git_proxy_stub).to receive(:git).with("--version").
+ expect(git_proxy_stub).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
index 53249116cd..20307f9c99 100644
--- a/spec/bundler/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -155,9 +155,9 @@ RSpec.describe Bundler::Fetcher::Dependency do
end
end
- shared_examples_for "the error suggests retrying with the full index" do
- it "should log the inability to fetch from API at debug level" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`")
+ shared_examples_for "the error is logged" do
+ it "should log the inability to fetch from API at debug level, and mention retrying" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
subject.specs(gem_names, full_dependency_list, last_spec_list)
end
end
@@ -166,25 +166,21 @@ RSpec.describe Bundler::Fetcher::Dependency do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::HTTPError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a GemspecError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::GemspecError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a MarshalError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::MarshalError.new } }
it_behaves_like "the error is properly handled"
-
- it "should log the inability to fetch from API and mention retrying" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
- subject.specs(gem_names, full_dependency_list, last_spec_list)
- end
+ it_behaves_like "the error is logged"
end
end
@@ -222,7 +218,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
it "should fetch dependencies from RubyGems and unmarshal them" do
expect(gem_names).to receive(:each_slice).with(rubygems_limit).and_call_original
expect(downloader).to receive(:fetch).with(dep_api_uri).and_return(fetch_response)
- expect(Bundler).to receive(:load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
+ expect(Bundler).to receive(:safe_load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
end
end
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index 94a0993a54..22aa3a8b0c 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -98,6 +98,16 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
+ context "when the request response is a Net::HTTPForbidden" do
+ let(:http_response) { Net::HTTPForbidden.new("1.1", 403, "Forbidden") }
+ let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
+
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ /Access token could not be authenticated for www.uri-to-fetch.com/)
+ end
+ end
+
context "when the request response is a Net::HTTPNotFound" do
let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }
@@ -178,26 +188,6 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response causes a NoMethodError" do
- before { allow(connection).to receive(:request).with(uri, net_http_get) { raise NoMethodError.new(message) } }
-
- context "and the error message is about use_ssl=" do
- let(:message) { "undefined method 'use_ssl='" }
-
- it "should raise a LoadError about openssl" do
- expect { subject.request(uri, options) }.to raise_error(LoadError, "cannot load such file -- openssl")
- end
- end
-
- context "and the error message is not about use_ssl=" do
- let(:message) { "undefined method 'undefined_method_call'" }
-
- it "should raise the original NoMethodError" do
- expect { subject.request(uri, options) }.to raise_error(NoMethodError, /undefined method 'undefined_method_call'/)
- end
- end
- end
-
context "when the request response causes a OpenSSL::SSL::SSLError" do
before { allow(connection).to receive(:request).with(uri, net_http_get) { raise OpenSSL::SSL::SSLError.new } }
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
index f0db07583c..971b64ce8f 100644
--- a/spec/bundler/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -63,26 +63,9 @@ RSpec.describe Bundler::Fetcher::Index do
context "when a 403 response occurs" do
let(:error_message) { "403" }
- before do
- allow(remote_uri).to receive(:userinfo).and_return(userinfo)
- end
-
- context "and there was userinfo" do
- let(:userinfo) { double(:userinfo) }
-
- it "should raise a Bundler::Fetcher::BadAuthenticationError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
- %r{Bad username or password for http://remote-uri.org})
- end
- end
-
- context "and there was no userinfo" do
- let(:userinfo) { nil }
-
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
- %r{Authentication is required for http://remote-uri.org})
- end
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ %r{Access token could not be authenticated for http://remote-uri.org})
end
end
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
index a104760075..27a63c476d 100644
--- a/spec/bundler/bundler/fetcher_spec.rb
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -159,4 +159,34 @@ RSpec.describe Bundler::Fetcher do
end
end
end
+
+ describe "#fetch_spec" do
+ let(:name) { "name" }
+ let(:version) { "1.3.17" }
+ let(:platform) { "platform" }
+ let(:downloader) { double("downloader") }
+ let(:body) { double(Net::HTTP::Get, :body => downloaded_data) }
+
+ context "when attempting to load a Gem::Specification" do
+ let(:spec) { Gem::Specification.new(name, version) }
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(spec)) }
+
+ it "returns the spec" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ result = fetcher.fetch_spec([name, version, platform])
+ expect(result).to eq(spec)
+ end
+ end
+
+ context "when attempting to load an unexpected class" do
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(3)) }
+
+ it "raises a HTTPError error" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ expect { fetcher.fetch_spec([name, version, platform]) }.to raise_error(Bundler::HTTPError, /Gemspec .* contained invalid data/i)
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
index 69fba7b826..37afe488f3 100644
--- a/spec/bundler/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -110,19 +110,6 @@ RSpec.describe Bundler, "friendly errors" do
it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new
end
- context "LoadError" do
- let(:error) { LoadError.new("cannot load such file -- openssl") }
-
- before do
- allow(error).to receive(:backtrace).and_return(["backtrace"])
- end
-
- it "Bundler.ui receive error" do
- expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL. LoadError: cannot load such file -- openssl\nbacktrace")
- Bundler::FriendlyErrors.log_error(error)
- end
- end
-
context "Interrupt" do
it "Bundler.ui receive error" do
expect(Bundler.ui).to receive(:error).with("\nQuitting...")
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
index 43a3630bbb..8058412f32 100644
--- a/spec/bundler/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -1,178 +1,162 @@
# frozen_string_literal: true
RSpec.describe Bundler::GemVersionPromoter do
- context "conservative resolver" do
- def versions(result)
- result.flatten.map(&:version).map(&:to_s)
+ let(:gvp) { described_class.new }
+
+ # Rightmost (highest array index) in result is most preferred.
+ # Leftmost (lowest array index) in result is least preferred.
+ # `build_candidates` has all versions of gem in index.
+ # `build_spec` is the version currently in the .lock file.
+ #
+ # In default (not strict) mode, all versions in the index will
+ # be returned, allowing Bundler the best chance to resolve all
+ # dependencies, but sometimes resulting in upgrades that some
+ # would not consider conservative.
+
+ describe "#sort_versions" do
+ def build_candidates(versions)
+ versions.map do |v|
+ Bundler::Resolver::Candidate.new(v)
+ end
end
- def make_instance(*args)
- @gvp = Bundler::GemVersionPromoter.new(*args).tap do |gvp|
- gvp.class.class_eval { public :filter_dep_specs, :sort_dep_specs }
- end
+ def build_package(name, version, locked = [])
+ Bundler::Resolver::Package.new(name, [], :locked_specs => Bundler::SpecSet.new(build_spec(name, version)), :unlock => locked)
end
- def unlocking(options)
- make_instance(Bundler::SpecSet.new([]), ["foo"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ def sorted_versions(candidates:, current:, name: "foo", locked: [])
+ gvp.sort_versions(
+ build_package(name, current, locked),
+ build_candidates(candidates)
+ ).flatten.map(&:version).map(&:to_s)
end
- def keep_locked(options)
- make_instance(Bundler::SpecSet.new([]), ["bar"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ it "numerically sorts versions" do
+ versions = sorted_versions(:candidates => %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0], :current => "1.7.8")
+ expect(versions).to eq %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]
end
- def build_spec_groups(name, versions)
- versions.map do |v|
- Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY], Gem::Platform::RUBY)
+ context "with no options" do
+ it "defaults to level=:major, strict=false, pre=false" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
end
end
- # Rightmost (highest array index) in result is most preferred.
- # Leftmost (lowest array index) in result is least preferred.
- # `build_spec_groups` has all versions of gem in index.
- # `build_spec` is the version currently in the .lock file.
- #
- # In default (not strict) mode, all versions in the index will
- # be returned, allowing Bundler the best chance to resolve all
- # dependencies, but sometimes resulting in upgrades that some
- # would not consider conservative.
- context "filter specs (strict) level patch" do
- it "when keeping build_spec, keep current, next release" do
- keep_locked(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.9 1.7.8]
- end
+ context "when strict" do
+ before { gvp.strict = true }
- it "when unlocking prefer next release first" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.8 1.7.9]
- end
+ context "when level is major" do
+ before { gvp.level = :major }
- it "when unlocking keep current when already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[1.7.9]
+ it "keeps downgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ end
end
- end
- context "filter specs (strict) level minor" do
- it "when unlocking favor next releases, remove minor and major increases" do
- unlocking(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "removes downgrades and major upgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.3.0 0.3.1 0.9.0]
+ end
end
- it "when keep locked, keep current, then favor next release, remove minor and major increases" do
- keep_locked(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.3.0 0.3.1 0.9.0 0.2.0]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "removes downgrades and major and minor upgrades" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.3.0 0.3.1]
+ end
end
end
- context "sort specs (not strict) level patch" do
- it "when not unlocking, same order but make sure build_spec version is most preferred to stay put" do
- keep_locked(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.5.4 1.6.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 2.0.0 2.0.1]),
- build_spec("foo", "1.7.7").first
- )
- expect(versions(res)).to eq %w[1.5.4 1.6.5 1.7.6 2.0.0 2.0.1 1.8.0 1.8.1 1.7.8 1.7.9 1.7.7]
- end
+ context "when not strict" do
+ before { gvp.strict = false }
- it "when unlocking favor next release, then current over minor increase" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9]
+ context "when level is major" do
+ before { gvp.level = :major }
+
+ it "orders by version" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ end
end
- it "when unlocking do proper integer comparison, not string" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9 1.7.15]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "favors downgrades, then upgrades by major descending, minor ascending, patch ascending" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 2.0.1 2.1.0 1.0.0 0.3.0 0.3.1 0.9.0]
+ end
end
- it "leave current when unlocking but already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[2.0.0 1.8.0 1.7.9]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "favors downgrades, then upgrades by major descending, minor descending, patch ascending" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
+ expect(versions).to eq %w[0.2.0 2.1.0 2.0.1 1.0.0 0.9.0 0.3.0 0.3.1]
+ end
end
end
- context "sort specs (not strict) level minor" do
- it "when unlocking favor next release, then minor increase over current" do
- unlocking(:level => :minor)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[2.0.0 2.0.1 1.0.0 0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when pre" do
+ before { gvp.pre = true }
+
+ it "sorts regardless of prerelease status" do
+ versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
+ expect(versions).to eq %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0]
end
end
- context "level error handling" do
- subject { Bundler::GemVersionPromoter.new }
+ context "when not pre" do
+ before { gvp.pre = false }
- it "should raise if not major, minor or patch is passed" do
- expect { subject.level = :minjor }.to raise_error ArgumentError
+ it "deprioritizes prerelease gems" do
+ versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
+ expect(versions).to eq %w[1.7.7.pre 1.8.1.pre 2.0.0.pre 1.8.0 1.8.1 2.0.0]
end
+ end
- it "should raise if invalid classes passed" do
- [123, nil].each do |value|
- expect { subject.level = value }.to raise_error ArgumentError
- end
+ context "when locking and not major" do
+ before { gvp.level = :minor }
+
+ it "keeps the current version last" do
+ versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], :current => "0.3.0", :locked => ["bar"])
+ expect(versions.last).to eq("0.3.0")
end
+ end
+ end
- it "should accept major, minor patch symbols" do
- [:major, :minor, :patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value
- end
+ describe "#level=" do
+ subject { described_class.new }
+
+ it "should raise if not major, minor or patch is passed" do
+ expect { subject.level = :minjor }.to raise_error ArgumentError
+ end
+
+ it "should raise if invalid classes passed" do
+ [123, nil].each do |value|
+ expect { subject.level = value }.to raise_error ArgumentError
end
+ end
- it "should accept major, minor patch strings" do
- %w[major minor patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value.to_sym
- end
+ it "should accept major, minor patch symbols" do
+ [:major, :minor, :patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value
end
end
- context "debug output" do
- it "should not kerblooie on its own debug output" do
- gvp = unlocking(:level => :patch)
- dep = Bundler::DepProxy.get_proxy(dep("foo", "1.2.0").first, "ruby")
- result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0]))
- expect(result.class).to eq Array
+ it "should accept major, minor patch strings" do
+ %w[major minor patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value.to_sym
end
end
end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
index 14a6a19a86..e86bdf009a 100644
--- a/spec/bundler/bundler/installer/gem_installer_spec.rb
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -12,7 +12,10 @@ RSpec.describe Bundler::GemInstaller do
context "spec_settings is nil" do
it "invokes install method with empty build_args" do
- allow(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => [], :previous_spec => nil)
+ allow(spec_source).to receive(:install).with(
+ spec,
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => [], :previous_spec => nil }
+ )
subject.install_from_spec
end
end
@@ -23,7 +26,10 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with(:inline)
allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
- expect(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"], :previous_spec => nil)
+ expect(spec_source).to receive(:install).with(
+ spec,
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"], :previous_spec => nil }
+ )
subject.install_from_spec
end
end
@@ -36,10 +42,7 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy --with-another-dummy-config")
expect(spec_source).to receive(:install).with(
spec,
- :force => false,
- :ensure_builtin_gems_cached => false,
- :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"],
- :previous_spec => nil
+ { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"], :previous_spec => nil }
)
subject.install_from_spec
end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
index e680633862..c8403a2e38 100644
--- a/spec/bundler/bundler/installer/parallel_installer_spec.rb
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -11,40 +11,6 @@ RSpec.describe Bundler::ParallelInstaller do
subject { described_class.new(installer, all_specs, size, standalone, force) }
- context "when dependencies that are not on the overall installation list are the only ones not installed" do
- let(:all_specs) do
- [
- build_spec("alpha", "1.0") {|s| s.runtime "a", "1" },
- ].flatten
- end
-
- it "prints a warning" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
- end
-
- context "when size > 1" do
- let(:size) { 500 }
-
- it "prints a warning and sets size to 1" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
- expect(subject.size).to eq(1)
- end
- end
- end
-
context "when the spec set is not a valid resolution" do
let(:all_specs) do
[
@@ -56,9 +22,9 @@ The missing gems are:
it "prints a warning" do
expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
Your lockfile doesn't include a valid resolution.
-You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies.
+You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies.
The unmet dependencies are:
-* diff-lcs (< 1.4), depended upon cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
+* diff-lcs (< 1.4), dependency of cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
W
subject.check_for_unmet_dependencies
end
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
index d34b0de342..5a7047459f 100644
--- a/spec/bundler/bundler/plugin/index_spec.rb
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -140,7 +140,7 @@ RSpec.describe Bundler::Plugin::Index do
describe "after conflict" do
let(:commands) { ["foo"] }
let(:sources) { ["bar"] }
- let(:hooks) { ["hoook"] }
+ let(:hooks) { ["thehook"] }
shared_examples "it cleans up" do
it "the path" do
@@ -156,7 +156,7 @@ RSpec.describe Bundler::Plugin::Index do
end
it "the hook" do
- expect(index.hook_plugins("xhoook")).to be_empty
+ expect(index.hook_plugins("xthehook")).to be_empty
end
end
@@ -164,7 +164,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
@@ -175,7 +175,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xthehook"])
end.to raise_error(Index::SourceConflict)
end
@@ -186,7 +186,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
index 8115e026d8..921a47a2d3 100644
--- a/spec/bundler/bundler/remote_specification_spec.rb
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Bundler::RemoteSpecification do
let(:platform) { "jruby" }
it "should return the spec name, version, and platform" do
- expect(subject.full_name).to eq("foo-1.0.0-jruby")
+ expect(subject.full_name).to eq("foo-1.0.0-java")
end
end
end
diff --git a/spec/bundler/bundler/resolver/candidate_spec.rb b/spec/bundler/bundler/resolver/candidate_spec.rb
new file mode 100644
index 0000000000..cd52c867c4
--- /dev/null
+++ b/spec/bundler/bundler/resolver/candidate_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Resolver::Candidate do
+ it "compares fine" do
+ version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version2 = described_class.new("1.12.5") # passing no specs creates a platform specific candidate, so sorts higher
+
+ expect(version2 >= version1).to be true
+
+ expect(version1.generic! == version2.generic!).to be true
+ expect(version1.platform_specific! == version2.platform_specific!).to be true
+
+ expect(version1.platform_specific! >= version2.generic!).to be true
+ expect(version2.platform_specific! >= version1.generic!).to be true
+
+ version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version2 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::X64_LINUX }])
+
+ expect(version2 >= version1).to be true
+ end
+end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
index bc1ca98457..0ba55e949f 100644
--- a/spec/bundler/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe Bundler::RubyDsl do
let(:dsl) { MockDSL.new }
let(:ruby_version) { "2.0.0" }
+ let(:ruby_version_arg) { ruby_version }
let(:version) { "2.0.0" }
let(:engine) { "jruby" }
let(:engine_version) { "9000" }
@@ -23,7 +24,10 @@ RSpec.describe Bundler::RubyDsl do
let(:invoke) do
proc do
- args = Array(ruby_version) + [options]
+ args = []
+ args << Array(ruby_version_arg) if ruby_version_arg
+ args << options
+
dsl.ruby(*args)
end
end
@@ -91,5 +95,26 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
end
+
+ context "with a file option" do
+ let(:options) { { :file => "foo" } }
+ let(:version) { "3.2.2" }
+ let(:ruby_version) { "3.2.2" }
+ let(:ruby_version_arg) { nil }
+ let(:engine_version) { version }
+ let(:patchlevel) { nil }
+ let(:engine) { "ruby" }
+ before { allow(Bundler).to receive(:read_file).with("foo").and_return("#{version}\n") }
+
+ it_behaves_like "it stores the ruby version"
+
+ context "and a version" do
+ let(:ruby_version_arg) { "2.0.0" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, "Cannot specify version when using the file option")
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
index f1df12294d..39d0571361 100644
--- a/spec/bundler/bundler/ruby_version_spec.rb
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -400,19 +400,19 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:bundler_system_ruby_version) { subject }
around do |example|
- if Bundler::RubyVersion.instance_variable_defined?("@ruby_version")
+ if Bundler::RubyVersion.instance_variable_defined?("@system")
begin
- old_ruby_version = Bundler::RubyVersion.instance_variable_get("@ruby_version")
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ old_ruby_version = Bundler::RubyVersion.instance_variable_get("@system")
+ Bundler::RubyVersion.remove_instance_variable("@system")
example.run
ensure
- Bundler::RubyVersion.instance_variable_set("@ruby_version", old_ruby_version)
+ Bundler::RubyVersion.instance_variable_set("@system", old_ruby_version)
end
else
begin
example.run
ensure
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ Bundler::RubyVersion.remove_instance_variable("@system")
end
end
end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
index 369f28fcef..182aa3646a 100644
--- a/spec/bundler/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -89,5 +89,16 @@ RSpec.describe Bundler::RubygemsIntegration do
expect(result).to eq(%w[specs prerelease_specs])
end
end
+
+ context "when loading an unexpected class" do
+ let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:unexpected_specs_response) { Marshal.dump(3) }
+
+ it "raises a MarshalError error" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).once.and_return(fetcher)
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(unexpected_specs_response)
+ expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror) }.to raise_error(Bundler::MarshalError, /unexpected class/i)
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 24e3de7ba8..4636993d9f 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Bundler::Settings do
"gem.mit" => "false",
"gem.test" => "minitest",
"thingy" => <<-EOS.tr("\n", " "),
---asdf --fdsa --ty=oh man i hope this doesnt break bundler because
+--asdf --fdsa --ty=oh man i hope this doesn't break bundler because
that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
--very-important-option=DontDeleteRoo
--very-important-option=DontDeleteRoo
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
index 68a24be31c..3c6536c4eb 100644
--- a/spec/bundler/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -246,6 +246,15 @@ RSpec.describe Bundler::SharedHelpers do
end
end
+ shared_examples_for "ENV['BUNDLER_SETUP'] gets set correctly" do
+ it "ensures bundler/setup is set in ENV['BUNDLER_SETUP']" do
+ skip "Does not play well with DidYouMean being a bundled gem instead of a default gem in Ruby 2.6" if RUBY_VERSION < "2.7"
+
+ subject.set_bundle_environment
+ expect(ENV["BUNDLER_SETUP"]).to eq("#{source_lib_dir}/bundler/setup")
+ end
+ end
+
shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
@@ -281,7 +290,7 @@ RSpec.describe Bundler::SharedHelpers do
if Gem.respond_to?(:path_separator)
allow(Gem).to receive(:path_separator).and_return(":")
else
- stub_const("File::PATH_SEPARATOR", ":".freeze)
+ stub_const("File::PATH_SEPARATOR", ":")
end
allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") }
expect { subject.send(:validate_bundle_path) }.to raise_error(
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index cffd72cc3f..c79dd8ff71 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -6,26 +6,31 @@ RSpec.describe Bundler::Source::Git::GitProxy do
let(:ref) { "HEAD" }
let(:revision) { nil }
let(:git_source) { nil }
+ let(:clone_result) { double(Process::Status, :success? => true) }
+ let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] }
subject { described_class.new(path, uri, ref, revision, git_source) }
context "with configured credentials" do
it "adds username and password to URI" do
Bundler.settings.temporary(uri => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "adds username and password to URI for host" do
Bundler.settings.temporary("github.com" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", uri, path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -34,7 +39,8 @@ RSpec.describe Bundler::Source::Git::GitProxy do
Bundler.settings.temporary("github.com" => "u:p") do
original = "https://orig:info@github.com/rubygems/rubygems.git"
subject = described_class.new(Pathname("path"), original, "HEAD")
- expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", original, path.to_s)
+ allow(subject).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(subject).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -43,7 +49,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
@@ -58,7 +64,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
@@ -73,7 +79,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
@@ -90,7 +96,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#full_version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
@@ -101,7 +107,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
@@ -112,7 +118,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(subject).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
@@ -122,33 +128,6 @@ RSpec.describe Bundler::Source::Git::GitProxy do
end
end
- describe "#copy_to" do
- let(:cache) { tmpdir("cache_path") }
- let(:destination) { tmpdir("copy_to_path") }
- let(:submodules) { false }
-
- context "when given a SHA as a revision" do
- let(:revision) { "abcd" * 10 }
- let(:command) { ["reset", "--hard", revision] }
- let(:command_for_display) { "git #{command.shelljoin}" }
-
- it "fails gracefully when resetting to the revision fails" do
- expect(subject).to receive(:git_retry).with("clone", any_args) { destination.mkpath }
- expect(subject).to receive(:git_retry).with("fetch", any_args, :dir => destination)
- expect(subject).to receive(:git).with(*command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command_for_display, destination))
- expect(subject).not_to receive(:git)
-
- expect { subject.copy_to(destination, submodules) }.
- to raise_error(
- Bundler::Source::Git::MissingGitRevisionError,
- "Git error: command `#{command_for_display}` in directory #{destination} has failed.\n" \
- "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?\n" \
- "If this error persists you could try removing the cache directory '#{destination}'"
- )
- end
- end
- end
-
it "doesn't allow arbitrary code execution through Gemfile uris with a leading dash" do
gemfile <<~G
gem "poc", git: "-u./pay:load.sh"
diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb
deleted file mode 100644
index 3ed899dbcf..0000000000
--- a/spec/bundler/bundler/vendored_persistent_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/vendored_persistent"
-
-RSpec.describe Bundler::PersistentHTTP do
- describe "#warn_old_tls_version_rubygems_connection" do
- let(:uri) { "https://index.rubygems.org" }
- let(:connection) { instance_double(Bundler::Persistent::Net::HTTP::Persistent::Connection) }
- let(:tls_version) { "TLSv1.2" }
- let(:socket) { double("Socket") }
- let(:socket_io) { double("SocketIO") }
-
- before do
- allow(connection).to receive_message_chain(:http, :use_ssl?).and_return(!tls_version.nil?)
- allow(socket).to receive(:io).and_return(socket_io) if socket
- connection.instance_variable_set(:@socket, socket)
-
- if tls_version
- allow(socket_io).to receive(:ssl_version).and_return(tls_version)
- end
- end
-
- shared_examples_for "does not warn" do
- it "does not warn" do
- allow(Bundler.ui).to receive(:warn).never
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- shared_examples_for "does warn" do |*expected|
- it "warns" do
- expect(Bundler.ui).to receive(:warn).with(*expected)
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- context "an HTTPS uri with TLSv1.2" do
- include_examples "does not warn"
- end
-
- context "without SSL" do
- let(:tls_version) { nil }
-
- include_examples "does not warn"
- end
-
- context "without a socket" do
- let(:socket) { nil }
-
- include_examples "does not warn"
- end
-
- context "with a different TLD" do
- let(:uri) { "https://foo.bar" }
- include_examples "does not warn"
-
- context "and an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does not warn"
- end
- end
-
- context "with a nonsense TLS version" do
- let(:tls_version) { "BlahBlah2.0Blah" }
- include_examples "does not warn"
- end
-
- context "with an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does warn",
- "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
- "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \
- "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
- :wrap => true
- end
- end
-end
diff --git a/spec/bundler/bundler/version_ranges_spec.rb b/spec/bundler/bundler/version_ranges_spec.rb
deleted file mode 100644
index bca044b0c0..0000000000
--- a/spec/bundler/bundler/version_ranges_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/version_ranges"
-
-RSpec.describe Bundler::VersionRanges do
- describe ".empty?" do
- shared_examples_for "empty?" do |exp, *req|
- it "returns #{exp} for #{req}" do
- r = Gem::Requirement.new(*req)
- ranges = described_class.for(r)
- expect(described_class.empty?(*ranges)).to eq(exp), "expected `#{r}` #{exp ? "" : "not "}to be empty"
- end
- end
-
- include_examples "empty?", false
- include_examples "empty?", false, "!= 1"
- include_examples "empty?", false, "!= 1", "= 2"
- include_examples "empty?", false, "!= 1", "> 1"
- include_examples "empty?", false, "!= 1", ">= 1"
- include_examples "empty?", false, "= 1", ">= 0.1", "<= 1.1"
- include_examples "empty?", false, "= 1", ">= 1", "<= 1"
- include_examples "empty?", false, "= 1", "~> 1"
- include_examples "empty?", false, ">= 0.z", "= 0"
- include_examples "empty?", false, ">= 0"
- include_examples "empty?", false, ">= 1.0.0", "< 2.0.0"
- include_examples "empty?", false, "~> 1"
- include_examples "empty?", false, "~> 2.0", "~> 2.1"
- include_examples "empty?", true, ">= 4.1.0", "< 5.0", "= 5.2.1"
- include_examples "empty?", true, "< 5.0", "< 5.3", "< 6.0", "< 6", "= 5.2.0", "> 2", ">= 3.0", ">= 3.1", ">= 3.2", ">= 4.0.0", ">= 4.1.0", ">= 4.2.0", ">= 4.2", ">= 4"
- include_examples "empty?", true, "!= 1", "< 2", "> 2"
- include_examples "empty?", true, "!= 1", "<= 1", ">= 1"
- include_examples "empty?", true, "< 2", "> 2"
- include_examples "empty?", true, "< 2", "> 2", "= 2"
- include_examples "empty?", true, "= 1", "!= 1"
- include_examples "empty?", true, "= 1", "= 2"
- include_examples "empty?", true, "= 1", "~> 2"
- include_examples "empty?", true, ">= 0", "<= 0.a"
- include_examples "empty?", true, "~> 2.0", "~> 3"
- end
-end
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index 7ea23cd312..890ba88605 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -90,7 +90,6 @@ RSpec.describe "bundle cache with git" do
expect(ref).not_to eq(old_ref)
bundle "update", :all => true
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
@@ -156,6 +155,9 @@ RSpec.describe "bundle cache with git" do
end
it "copies repository to vendor cache, including submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
git = build_git "has_submodule", "1.0" do |s|
@@ -218,4 +220,57 @@ RSpec.describe "bundle cache with git" do
expect(the_bundle).to include_gem "foo 1.0"
end
end
+
+ it "respects the --no-install flag" do
+ git = build_git "foo", &:add_c_extension
+ ref = git.ref_for("main", 11)
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle "config set cache_all true"
+
+ # The algorithm for the cache location for a git checkout is
+ # in Bundle::Source::Git#cache_path
+ cache_path_name = "foo-1.0-#{Digest(:SHA1).hexdigest(lib_path("foo-1.0").to_s)}"
+
+ # Run this test twice. This is because materially different codepaths
+ # will get hit the second time around.
+ # The first time, Bundler::Sources::Git#install_path is set to the system
+ # wide cache directory bundler/gems; the second time, it's set to the
+ # vendor/cache directory. We don't want the native extension to appear in
+ # either of these places, so run the `bundle cache` command twice.
+ 2.times do
+ bundle :cache, "all-platforms" => true, :install => false
+
+ # it did _NOT_ actually install the gem - neither in $GEM_HOME (bundler 2 mode),
+ # nor in .bundle (bundler 3 mode)
+ expect(Pathname.new(File.join(default_bundle_path, "gems/foo-1.0-#{ref}"))).to_not exist
+ # it _did_ cache the gem in vendor/
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ # it did _NOT_ build the gems extensions in the vendor/ dir
+ expect(Dir[bundled_app("vendor/cache/foo-1.0-#{ref}/lib/foo_c*")]).to be_empty
+ # it _did_ cache the git checkout
+ expect(default_cache_path("git", cache_path_name)).to exist
+ # And the checkout is a bare checkout
+ expect(default_cache_path("git", cache_path_name, "HEAD")).to exist
+ end
+
+ # Subsequently installing the gem should compile it.
+ # _currently_, the gem gets compiled in vendor/cache, and vendor/cache is added
+ # to the $LOAD_PATH for git extensions, so it all kind of "works". However, in the
+ # future we would like to stop adding vendor/cache to the $LOAD_PATH for git extensions
+ # and instead treat them identically to normal gems (where the gem install location,
+ # not the cache location, is added to $LOAD_PATH).
+ # Verify that the compilation worked and the result is in $LOAD_PATH by simply attempting
+ # to require it; that should make sure this spec does not break if the load path behaviour
+ # is changed.
+ bundle :install, :local => true
+ ruby <<~R, :raise_on_error => false
+ require 'bundler/setup'
+ require 'foo_c'
+ R
+ expect(last_command).to_not be_failure
+ end
end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
index 96ea238063..5a5b534e8d 100644
--- a/spec/bundler/commands/add_spec.rb
+++ b/spec/bundler/commands/add_spec.rb
@@ -103,6 +103,15 @@ RSpec.describe "bundle add" do
end
end
+ describe "with --path" do
+ it "adds dependency with specified path" do
+ bundle "add 'foo' --path='#{lib_path("foo-2.0")}'"
+
+ expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :path => "#{lib_path("foo-2.0")}"/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
describe "with --git" do
it "adds dependency with specified git source" do
bundle "add foo --git=#{lib_path("foo-2.0")}"
@@ -135,7 +144,7 @@ RSpec.describe "bundle add" do
end
describe "with --github" do
- it "adds dependency with specified github source" do
+ it "adds dependency with specified github source", :realworld do
bundle "add rake --github=ruby/rake"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake"})
@@ -143,7 +152,7 @@ RSpec.describe "bundle add" do
end
describe "with --github and --branch" do
- it "adds dependency with specified github source and branch" do
+ it "adds dependency with specified github source and branch", :realworld do
bundle "add rake --github=ruby/rake --branch=master"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :branch => "master"})
@@ -151,7 +160,7 @@ RSpec.describe "bundle add" do
end
describe "with --github and --ref" do
- it "adds dependency with specified github source and ref" do
+ it "adds dependency with specified github source and ref", :realworld do
bundle "add rake --github=ruby/rake --ref=5c60da8"
expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :ref => "5c60da8"})
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index 2634f43417..bfbef58181 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -166,6 +166,21 @@ RSpec.describe "bundle binstubs <gem>" do
end
end
+ context "and the version is newer when given `gems.rb` and `gems.locked`" do
+ before do
+ gemfile bundled_app("gems.rb"), gemfile
+ lockfile bundled_app("gems.locked"), lockfile.gsub(system_bundler_version, "999.999")
+ end
+
+ it "runs the correct version of bundler" do
+ sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+
+ expect(exitstatus).to eq(42)
+ expect(err).to include("Activating bundler (~> 999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
+ end
+ end
+
context "and the version is older and a different major" do
let(:system_bundler_version) { "55" }
@@ -181,6 +196,22 @@ RSpec.describe "bundle binstubs <gem>" do
end
end
+ context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do
+ let(:system_bundler_version) { "55" }
+
+ before do
+ gemfile bundled_app("gems.rb"), gemfile
+ lockfile bundled_app("gems.locked"), lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 44.0")
+ end
+
+ it "runs the correct version of bundler" do
+ sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+ expect(exitstatus).to eq(42)
+ expect(err).to include("Activating bundler (~> 44.0) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
+ end
+ end
+
context "and the version is older and the same major" do
let(:system_bundler_version) { "2.999.999" }
@@ -188,7 +219,7 @@ RSpec.describe "bundle binstubs <gem>" do
lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 2.3.0")
end
- it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev" do
+ it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev", :realworld => true do
sys_exec "bin/bundle install --verbose", :artifice => "vcr"
expect(exitstatus).not_to eq(42)
expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.")
@@ -224,7 +255,7 @@ RSpec.describe "bundle binstubs <gem>" do
context "when update --bundler is called" do
before { lockfile.gsub(system_bundler_version, "1.1.1") }
- it "calls through to the latest bundler version" do
+ it "calls through to the latest bundler version", :realworld do
sys_exec "bin/bundle update --bundler", :env => { "DEBUG" => "1" }
using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out)
expect(using_bundler_line).to_not be_nil
@@ -369,6 +400,7 @@ RSpec.describe "bundle binstubs <gem>" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
+ gem "rails"
G
end
@@ -396,6 +428,26 @@ RSpec.describe "bundle binstubs <gem>" do
expect(bundled_app("bin/rackup.cmd")).to exist
end
end
+
+ context "when the gem is bundler" do
+ it "warns without generating a standalone binstub" do
+ bundle "binstubs bundler --standalone"
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).to include("Sorry, Bundler can only be run via RubyGems.")
+ end
+ end
+
+ context "when specified --all option" do
+ it "generates standalone binstubs for all gems except bundler" do
+ bundle "binstubs --standalone --all"
+ expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).not_to include("Sorry, Bundler can only be run via RubyGems.")
+ end
+ end
end
context "when the bin already exists" do
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index 356a658e7c..a9ed389233 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -291,7 +291,7 @@ RSpec.describe "bundle cache" do
G
subject
expect(exitstatus).to eq(16)
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
bundle "env"
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index b4996977c1..99a858e9e9 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -172,7 +172,7 @@ RSpec.describe "bundle check" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
@@ -203,7 +203,7 @@ RSpec.describe "bundle check" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
@@ -351,7 +351,7 @@ RSpec.describe "bundle check" do
PLATFORMS
ruby
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
index 76c7e2adec..471cd6c354 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -625,7 +625,7 @@ RSpec.describe "bundle clean" do
expect(out).to eq("1.0")
end
- it "when using --force, it doesn't remove default gem binaries" do
+ it "when using --force, it doesn't remove default gem binaries", :realworld do
skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
@@ -638,11 +638,6 @@ RSpec.describe "bundle clean" do
s.executables = "irb"
end
- if Gem.win_platform? && RUBY_VERSION < "3.1.0"
- default_fiddle_version = ruby "require 'fiddle'; puts Gem.loaded_specs['fiddle'].version"
- realworld_system_gems "fiddle --version #{default_fiddle_version}"
- end
-
realworld_system_gems "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1"
install_gemfile <<-G
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index 6148b1c7ce..ede93f99eb 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -143,17 +143,15 @@ RSpec.describe ".bundle/config" do
end
it "has lower precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
+ ENV["BUNDLE_FOO"] = "env"
- bundle "config set --global foo global"
- expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
+ bundle "config set --global foo global"
+ expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("env")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("env")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
@@ -221,15 +219,13 @@ RSpec.describe ".bundle/config" do
end
it "has higher precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
- bundle "config set --local foo local"
-
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("local")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ ENV["BUNDLE_FOO"] = "env"
+ bundle "config set --local foo local"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index 1eeb276105..1afac00923 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -134,7 +134,7 @@ RSpec.describe "bundle doctor" do
end
end
- context "when home contains filesname with special characters" do
+ context "when home contains filenames with special characters" do
it "escape filename before command execute" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
index c6947afeae..099cdb39fa 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -2,11 +2,10 @@
RSpec.describe "bundle exec" do
let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
- before :each do
- system_gems(system_gems_to_install, :path => default_bundle_path)
- end
it "works with --gemfile flag" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
create_file "CustomGemfile", <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "1.0.0"
@@ -17,6 +16,8 @@ RSpec.describe "bundle exec" do
end
it "activates the correct gem" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
@@ -27,6 +28,8 @@ RSpec.describe "bundle exec" do
end
it "works and prints no warnings when HOME is not writable" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
@@ -209,8 +212,6 @@ RSpec.describe "bundle exec" do
end
context "with default gems" do
- let(:system_gems_to_install) { [] }
-
let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false }
context "when not specified in Gemfile" do
@@ -402,6 +403,8 @@ RSpec.describe "bundle exec" do
end
it "raises a helpful error when exec'ing to something outside of the bundle" do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
bundle "config set clean false" # want to keep the rackup binstub
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -706,6 +709,8 @@ RSpec.describe "bundle exec" do
RUBY
before do
+ system_gems(system_gems_to_install, :path => default_bundle_path)
+
bundled_app(path).open("w") {|f| f << executable }
bundled_app(path).chmod(0o755)
@@ -911,6 +916,30 @@ Run `bundle install` to install missing gems.
end
end
+ context "when Bundler.setup fails and Gemfile is not the default" do
+ before do
+ create_file "CustomGemfile", <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ ENV["BUNDLE_GEMFILE"] = "CustomGemfile"
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { "" }
+
+ it "prints proper suggestion" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.")
+ expect(out).to eq(expected)
+ end
+ end
+
context "when the executable exits non-zero via at_exit" do
let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" }
let(:exit_code) { 1 }
diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb
index 5a0c5411da..5415b88eeb 100644
--- a/spec/bundler/commands/fund_spec.rb
+++ b/spec/bundler/commands/fund_spec.rb
@@ -5,20 +5,20 @@ RSpec.describe "bundle fund" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
index f72763900e..409c49f9e1 100644
--- a/spec/bundler/commands/help_spec.rb
+++ b/spec/bundler/commands/help_spec.rb
@@ -23,8 +23,8 @@ RSpec.describe "bundle help" do
end
it "still outputs the old help for commands that do not have man pages yet" do
- bundle "help version"
- expect(out).to include("Prints the bundler's version information")
+ bundle "help fund"
+ expect(out).to include("Lists information about gems seeking funding assistance")
end
it "looks for a binary and executes it with --help option if it's named bundler-<task>" do
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index e4b970eb34..851f50240f 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -6,13 +6,13 @@ RSpec.describe "bundle info" do
build_repo2 do
build_gem "has_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
end
@@ -215,7 +215,7 @@ RSpec.describe "bundle info" do
G
bundle "info rac"
- expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/)
+ expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/)
end
end
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
index 683a453c7d..6aa3e9edd1 100644
--- a/spec/bundler/commands/init_spec.rb
+++ b/spec/bundler/commands/init_spec.rb
@@ -7,6 +7,29 @@ RSpec.describe "bundle init" do
expect(bundled_app_gemfile).to be_file
end
+ context "with a template with permission flags not matching current process umask" do
+ let(:template_file) do
+ gemfile = Bundler.preferred_gemfile_name
+ templates_dir.join(gemfile)
+ end
+
+ let(:target_dir) { bundled_app("init_permissions_test") }
+
+ around do |example|
+ old_chmod = File.stat(template_file).mode
+ FileUtils.chmod(old_chmod | 0o111, template_file) # chmod +x
+ example.run
+ FileUtils.chmod(old_chmod, template_file)
+ end
+
+ it "honours the current process umask when generating from a template" do
+ FileUtils.mkdir(target_dir)
+ bundle :init, :dir => target_dir
+ generated_mode = File.stat(File.join(target_dir, "Gemfile")).mode & 0o111
+ expect(generated_mode).to be_zero
+ end
+ end
+
context "when a Gemfile already exists" do
before do
create_file "Gemfile", <<-G
@@ -42,7 +65,7 @@ RSpec.describe "bundle init" do
context "when the dir is not writable by the current user" do
let(:subdir) { "child_dir" }
- it "notifies the user that it can not write to it" do
+ it "notifies the user that it cannot write to it" do
FileUtils.mkdir bundled_app(subdir)
# chmod a-w it
mode = File.stat(bundled_app(subdir)).mode ^ 0o222
@@ -168,4 +191,17 @@ RSpec.describe "bundle init" do
end
end
end
+
+ describe "using the --gemfile" do
+ it "should use the --gemfile value to name the gemfile" do
+ custom_gemfile_name = "NiceGemfileName"
+
+ bundle :init, :gemfile => custom_gemfile_name
+
+ expect(out).to include("Writing new #{custom_gemfile_name}")
+ used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile")
+ generated_gemfile = bundled_app(custom_gemfile_name).read
+ expect(generated_gemfile).to eq(used_template)
+ end
+ end
end
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
index 92e86bd6cc..d711fe010d 100644
--- a/spec/bundler/commands/inject_spec.rb
+++ b/spec/bundler/commands/inject_spec.rb
@@ -109,7 +109,7 @@ Usage: "bundle inject GEM VERSION"
gem "rack-obama"
G
bundle "inject 'rack' '> 0'", :raise_on_error => false
- expect(err).to match(/trying to install in deployment mode after changing/)
+ expect(err).to match(/the lockfile can't be updated because frozen mode is set/)
expect(bundled_app_lock.read).not_to match(/rack-obama/)
end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index 7bf36ee020..f572bdf176 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -285,7 +285,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "installs gems for windows" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -293,7 +293,7 @@ RSpec.describe "bundle install with gem sources" do
G
run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 MSWIN")
+ expect(out).to eq("1.0 x86-mswin32")
end
end
@@ -1076,4 +1076,31 @@ RSpec.describe "bundle install with gem sources" do
G
end
end
+
+ context "when a gem has equivalent versions with inconsistent dependencies" do
+ before do
+ build_repo4 do
+ build_gem "autobuild", "1.10.rc2" do |s|
+ s.add_dependency "utilrb", ">= 1.6.0"
+ end
+
+ build_gem "autobuild", "1.10.0.rc2" do |s|
+ s.add_dependency "utilrb", ">= 2.0"
+ end
+ end
+ end
+
+ it "does not crash unexpectedly" do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "autobuild", "1.10.rc2"
+ G
+
+ bundle "install --jobs 1", :raise_on_error => false
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ expect(err).to include("Could not find compatible versions")
+ end
+ end
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index b314169a98..491fa30949 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -140,6 +140,62 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile)
end
+ it "does not unlock git sources when only uri shape changes" do
+ build_git("foo")
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ G
+
+ # Change uri format to end with "/" and reinstall
+ install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}/"
+ G
+
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies because the list of sources changed")
+ end
+
+ it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do
+ ref = build_git("foo").ref_for("HEAD")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rake"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{file_uri_for(lib_path("foo-1.0"))}
+ revision: #{ref}
+ branch: deadbeef
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rake (10.0.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update rake --verbose"
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(lockfile).to include("rake (13.0.1)")
+ end
+
it "errors when updating a missing specific gems using --update" do
lockfile @lockfile
@@ -176,7 +232,10 @@ RSpec.describe "bundle lock" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
@@ -210,6 +269,55 @@ RSpec.describe "bundle lock" do
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "lock --update --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+
+ it "patch preferred" do
+ bundle "lock --update --patch --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
+ end
+
+ it "minor preferred" do
+ bundle "lock --update --minor --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
+ end
+
+ it "major preferred" do
+ bundle "lock --update --major --pre"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+ end
+ end
+
+ it "updates the bundler version in the lockfile to the latest bundler version" do
+ build_repo4 do
+ build_gem "bundler", "55"
+ end
+
+ system_gems "bundler-55", :gem_repo => gem_repo4
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "https://gems.repo4"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ bundle "lock --update --bundler --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
+
+ update_repo4 do
+ build_gem "bundler", "99"
+ end
+
+ bundle "lock --update --bundler --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
end
it "supports adding new platforms" do
@@ -217,7 +325,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
end
it "supports adding new platforms with force_ruby_platform = true" do
@@ -226,11 +334,11 @@ RSpec.describe "bundle lock" do
remote: #{file_uri_for(gem_repo1)}/
specs:
platform_specific (1.0)
- platform_specific (1.0-x86-linux)
+ platform_specific (1.0-x86-64_linux)
PLATFORMS
ruby
- x86-linux
+ x86_64-linux
DEPENDENCIES
platform_specific
@@ -241,7 +349,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to contain_exactly(rb, linux, java, mingw)
+ expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
end
it "supports adding the `ruby` platform" do
@@ -249,7 +357,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(["ruby", specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array(["ruby", local_platform].uniq)
end
it "warns when adding an unknown platform" do
@@ -262,16 +370,71 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
bundle "lock --remove-platform java"
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([mingw, specific_local_platform].uniq)
+ expect(lockfile.platforms).to match_array([x86_mingw32, local_platform].uniq)
+ end
+
+ it "also cleans up redundant platform gems when removing platforms" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock --remove-platform ruby"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "errors when removing all platforms" do
- bundle "lock --remove-platform #{specific_local_platform}", :raise_on_error => false
+ bundle "lock --remove-platform #{local_platform}", :raise_on_error => false
expect(err).to include("Removing all platforms from the bundle is not allowed")
end
@@ -280,7 +443,7 @@ RSpec.describe "bundle lock" do
build_repo4 do
build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s|
- s.platform = mingw
+ s.platform = x86_mingw32
end
build_gem "gssapi", "0.1"
@@ -312,7 +475,7 @@ RSpec.describe "bundle lock" do
gem "gssapi"
G
- simulate_platform(mingw) { bundle :lock }
+ simulate_platform(x86_mingw32) { bundle :lock }
expect(lockfile).to eq <<~G
GEM
@@ -540,6 +703,62 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end
+ it "does not crash on conflicting ruby requirements between platform versions in two different gems" do
+ build_repo4 do
+ build_gem "unf_ext", "0.0.8.2"
+
+ build_gem "unf_ext", "0.0.8.2" do |s|
+ s.required_ruby_version = [">= 2.4", "< #{previous_ruby_minor}"]
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "unf_ext", "0.0.8.2" do |s|
+ s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
+ s.platform = "x64-mingw-ucrt"
+ end
+
+ build_gem "google-protobuf", "3.21.12"
+
+ build_gem "google-protobuf", "3.21.12" do |s|
+ s.required_ruby_version = [">= 2.5", "< #{previous_ruby_minor}"]
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "google-protobuf", "3.21.12" do |s|
+ s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
+ s.platform = "x64-mingw-ucrt"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "google-protobuf"
+ gem "unf_ext"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ google-protobuf (3.21.12)
+ unf_ext (0.0.8.2)
+
+ PLATFORMS
+ x64-mingw-ucrt
+ x64-mingw32
+
+ DEPENDENCIES
+ google-protobuf
+ unf_ext
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" }
+ end
+
it "respects lower bound ruby requirements" do
build_repo4 do
build_gem "our_private_gem", "0.1.0" do |s|
@@ -595,4 +814,460 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
end
end
+
+ context "when a system gem has incorrect dependencies, different from the lockfile" do
+ before do
+ build_repo4 do
+ build_gem "debug", "1.6.3" do |s|
+ s.add_dependency "irb", ">= 1.3.6"
+ end
+
+ build_gem "irb", "1.5.0"
+ end
+
+ system_gems "irb-1.5.0", :gem_repo => gem_repo4
+ system_gems "debug-1.6.3", :gem_repo => gem_repo4
+
+ # simulate gemspec with wrong empty dependencies
+ debug_gemspec_path = system_gem_path("specifications/debug-1.6.3.gemspec")
+ debug_gemspec = Gem::Specification.load(debug_gemspec_path.to_s)
+ debug_gemspec.dependencies.clear
+ File.write(debug_gemspec_path, debug_gemspec.to_ruby)
+ end
+
+ it "respects the existing lockfile, even when reresolving" do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "debug"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ arm64-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "properly shows resolution errors including OR requirements" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "railties", ">= 6.1", "< 7.1"
+ end
+ build_gem "actionpack", "6.1.4"
+ build_gem "actionpack", "7.0.3.1"
+ build_gem "actionpack", "7.0.4"
+ build_gem "railties", "6.1.4" do |s|
+ s.add_dependency "actionpack", "6.1.4"
+ end
+ build_gem "rails", "7.0.3.1" do |s|
+ s.add_dependency "railties", "7.0.3.1"
+ end
+ build_gem "rails", "7.0.4" do |s|
+ s.add_dependency "railties", "7.0.4"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", ">= 7.0.3.1"
+ gem "activeadmin", "2.13.1"
+ G
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.4 depends on railties = 7.0.4
+ and rails < 7.0.4 depends on railties = 7.0.3.1,
+ railties = 7.0.3.1 OR = 7.0.4 is required.
+ So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally,
+ version solving has failed.
+ ERR
+ end
+
+ it "is able to display some explanation on crazy irresolvable cases" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "ransack", "= 3.1.0"
+ end
+
+ # Activemodel is missing as a dependency in lockfile
+ build_gem "ransack", "3.1.0" do |s|
+ s.add_dependency "activemodel", ">= 6.0.4"
+ s.add_dependency "activesupport", ">= 6.0.4"
+ end
+
+ %w[6.0.4 7.0.2.3 7.0.3.1 7.0.4].each do |version|
+ build_gem "activesupport", version
+
+ # Activemodel is only available on 6.0.4
+ if version == "6.0.4"
+ build_gem "activemodel", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ end
+
+ build_gem "rails", version do |s|
+ # Depednencies of Rails 7.0.2.3 are in reverse order
+ if version == "7.0.2.3"
+ s.add_dependency "activesupport", version
+ s.add_dependency "activemodel", version
+ else
+ s.add_dependency "activemodel", version
+ s.add_dependency "activesupport", version
+ end
+ end
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", ">= 7.0.2.3"
+ gem "activeadmin", "= 2.13.1"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ activeadmin (2.13.1)
+ ransack (= 3.1.0)
+ ransack (3.1.0)
+ activemodel (>= 6.0.4)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activeadmin (= 2.13.1)
+ ransack (= 3.1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because every version of activemodel depends on activesupport = 6.0.4
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3,
+ every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1.
+ And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.3.1 cannot be used.
+ (1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1
+ and rails >= 7.0.4 depends on activemodel = 7.0.4,
+ rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4.
+
+ Because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3
+ and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1,
+ rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 or activesupport = 7.0.3.1.
+ And because rails >= 7.0.4 depends on activesupport = 7.0.4
+ and every version of activemodel depends on activesupport = 6.0.4,
+ activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3.
+ And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1),
+ rails >= 7.0.2.3 cannot be used.
+ So, because Gemfile depends on rails >= 7.0.2.3,
+ version solving has failed.
+ ERR
+
+ lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle "lock", :raise_on_error => false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 OR = 7.0.3.1.
+ And because every version of activemodel depends on activesupport = 6.0.4,
+ rails >= 7.0.2.3, < 7.0.4 requires activesupport = 6.0.4.
+ Because rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1
+ and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3,
+ rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1.
+ Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used.
+ And because rails >= 7.0.4 depends on activemodel = 7.0.4,
+ rails >= 7.0.2.3 requires activemodel = 7.0.4.
+ So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally
+ and Gemfile depends on rails >= 7.0.2.3,
+ version solving has failed.
+ ERR
+ end
+
+ it "does not accidentally resolves to prereleases" do
+ build_repo4 do
+ build_gem "autoproj", "2.0.3" do |s|
+ s.add_dependency "autobuild", ">= 1.10.0.a"
+ s.add_dependency "tty-prompt"
+ end
+
+ build_gem "tty-prompt", "0.6.0"
+ build_gem "tty-prompt", "0.7.0"
+
+ build_gem "autobuild", "1.10.0.b3"
+ build_gem "autobuild", "1.10.1" do |s|
+ s.add_dependency "tty-prompt", "~> 0.6.0"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "autoproj", ">= 2.0.0"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("autobuild (1.10.0.b3)")
+ expect(lockfile).to include("autobuild (1.10.1)")
+ end
+
+ # Newer rails depends on Bundler, while ancient Rails does not. Bundler tries
+ # a first resolution pass that does not consider pre-releases. However, when
+ # using a pre-release Bundler (like the .dev version), that results in that
+ # pre-release being ignored and resolving to a version that does not depend on
+ # Bundler at all. We should avoid that and still consider .dev Bundler.
+ #
+ it "does not ignore prereleases with there's only one candidate" do
+ build_repo4 do
+ build_gem "rails", "7.4.0.2" do |s|
+ s.add_dependency "bundler", ">= 1.15.0"
+ end
+
+ build_gem "rails", "2.3.18"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rails"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("rails (2.3.18)")
+ expect(lockfile).to include("rails (7.4.0.2)")
+ end
+
+ it "deals with platform specific incompatibilities" do
+ build_repo4 do
+ build_gem "activerecord", "6.0.6"
+ build_gem "activerecord-jdbc-adapter", "60.4" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.0.0"
+ end
+ build_gem "activerecord-jdbc-adapter", "61.0" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.1.0"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "activerecord", "6.0.6"
+ gem "activerecord-jdbc-adapter", "61.0"
+ G
+
+ simulate_platform "universal-java-19" do
+ bundle "lock", :raise_on_error => false
+ end
+
+ expect(err).to include("Could not find compatible versions")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ end
+
+ context "when re-resolving to include prereleases" do
+ before do
+ build_repo4 do
+ build_gem "tzinfo-data", "1.2022.7"
+ build_gem "rails", "7.1.0.alpha" do |s|
+ s.add_dependency "activesupport"
+ end
+ build_gem "activesupport", "7.1.0.alpha"
+ end
+ end
+
+ it "does not end up including gems scoped to other platforms in the lockfile" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rails"
+ gem "tzinfo-data", platform: :windows
+ G
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).not_to include("tzinfo-data (1.2022.7)")
+ end
+ end
+
+ context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do
+ before do
+ build_lib "foo", :path => bundled_app do |s|
+ s.add_dependency "nokogiri"
+ end
+
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec
+ G
+ end
+
+ it "locks ruby specs" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ nokogiri
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.2)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when adding a new gem that requires unlocking other transitive deps" do
+ before do
+ build_repo4 do
+ build_gem "govuk_app_config", "0.1.0"
+
+ build_gem "govuk_app_config", "4.13.0" do |s|
+ s.add_dependency "railties", ">= 5.0"
+ end
+
+ %w[7.0.4.1 7.0.4.3].each do |v|
+ build_gem "railties", v do |s|
+ s.add_dependency "actionpack", v
+ s.add_dependency "activesupport", v
+ end
+
+ build_gem "activesupport", v
+ build_gem "actionpack", v
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "govuk_app_config"
+ gem "activesupport", "7.0.4.3"
+ G
+
+ # Simulate out of sync lockfile because top level dependency on
+ # activesuport has just been added to the Gemfile, and locked to a higher
+ # version
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ actionpack (7.0.4.1)
+ activesupport (7.0.4.1)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.1)
+ actionpack (= 7.0.4.1)
+ activesupport (= 7.0.4.1)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ govuk_app_config
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not downgrade top level dependencies" do
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ actionpack (7.0.4.3)
+ activesupport (7.0.4.3)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.3)
+ actionpack (= 7.0.4.3)
+ activesupport (= 7.0.4.3)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ activesupport (= 7.0.4.3)
+ govuk_app_config
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index 55a04b69c5..ede1ff6b8e 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -310,30 +310,30 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=minitest --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=rspec --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext=c, --test=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop"
+ bundle "gem #{gem_name} --ext=c --test=test-unit --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
@@ -345,10 +345,45 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
+ it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=minitest --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=rspec --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle "gem #{gem_name} --ext=rust --test=test-unit --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
shared_examples_for "CI config is absent" do
it "does not create any CI files" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
@@ -598,11 +633,19 @@ RSpec.describe "bundle gem" do
it "does not include the gemspec file in files" do
bundle "gem #{gem_name}"
- bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
end
+ it "does not include the Gemfile file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("Gemfile")
+ end
+
it "runs rake without problems" do
bundle "gem #{gem_name}"
@@ -888,12 +931,29 @@ RSpec.describe "bundle gem" do
bundle "gem #{gem_name}"
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
end
+ context "--ci set to travis" do
+ it "generates a GitHub Actions config file" do
+ bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
+ end
+ end
+
+ context "--ci set to travis" do
+ it "generates a GitHub Actions config file" do
+ bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
+ end
+ end
+
context "--ci set to github" do
it "generates a GitHub Actions config file" do
bundle "gem #{gem_name} --ci=github"
@@ -918,38 +978,32 @@ RSpec.describe "bundle gem" do
end
end
- context "--ci set to travis" do
- it "generates a Travis CI config file" do
- bundle "gem #{gem_name} --ci=travis"
-
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
- end
- end
-
context "gem.ci setting set to none" do
it "doesn't generate any CI config" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
end
- context "gem.ci setting set to github" do
- it "generates a GitHub Actions config file" do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name}"
+ context "gem.ci setting set to travis" do
+ it "errors with friendly message" do
+ bundle "config set gem.ci travis"
+ bundle "gem #{gem_name}", :raise_on_error => false
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ expect(err).to include("Support for Travis CI was removed from gem skeleton generator,")
+ expect(err).to include("bundle config unset gem.ci")
+
+ expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
end
end
- context "gem.ci setting set to travis" do
- it "generates a Travis CI config file" do
- bundle "config set gem.ci travis"
+ context "gem.ci setting set to github" do
+ it "generates a GitHub Actions config file" do
+ bundle "config set gem.ci github"
bundle "gem #{gem_name}"
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
end
end
@@ -1322,21 +1376,46 @@ RSpec.describe "bundle gem" do
include_examples "generating a gem"
- context "--ext parameter set" do
- let(:flags) { "--ext" }
+ context "--ext parameter with no value" do
+ context "is deprecated", :bundler => "< 3" do
+ it "prints deprecation when used after gem name" do
+ bundle ["gem", "--ext", gem_name].compact.join(" ")
+ expect(err).to include "[DEPRECATED]"
+ expect(err).to include "`--ext` with no arguments has been deprecated"
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ end
+
+ it "prints deprecation when used before gem name" do
+ bundle ["gem", gem_name, "--ext"].compact.join(" ")
+ expect(err).to include "[DEPRECATED]"
+ expect(err).to include "`--ext` with no arguments has been deprecated"
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ end
+ end
+ end
+
+ context "--ext parameter set with C" do
+ let(:flags) { "--ext=c" }
before do
bundle ["gem", gem_name, flags].compact.join(" ")
end
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
it "builds ext skeleton" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
- it "includes rake-compiler" do
+ it "includes rake-compiler, but no Rust related changes" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+
+ expect(bundled_app("#{gem_name}/Gemfile").read).to_not include('gem "rb_sys"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
@@ -1358,6 +1437,64 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
end
+
+ context "--ext parameter set with rust and old RubyGems" do
+ it "fails in friendly way" do
+ if ::Gem::Version.new("3.3.11") <= ::Gem.rubygems_version
+ skip "RubyGems compatible with Rust builder"
+ end
+
+ expect do
+ bundle ["gem", gem_name, "--ext=rust"].compact.join(" ")
+ end.to raise_error(RuntimeError, /too old to build Rust extension/)
+ end
+ end
+
+ context "--ext parameter set with rust" do
+ let(:flags) { "--ext=rust" }
+
+ before do
+ skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
+
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
+ end
+
+ it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do
+ expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+ expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rb_sys"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ')
+ end
+
+ it "depends on compile task for build" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ # frozen_string_literal: true
+
+ require "bundler/gem_tasks"
+ require "rb_sys/extensiontask"
+
+ task build: :compile
+
+ RbSys::ExtensionTask.new("#{gem_name}") do |ext|
+ ext.lib_dir = "lib/#{gem_name}"
+ end
+
+ task default: :compile
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
+ end
+ end
end
context "gem naming with dashed", :readline do
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index 85f15176b4..e30ebfea5b 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -58,6 +58,64 @@ RSpec.describe "bundle open" do
expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
+ it "opens subpath of the gem" do
+ bundle "open activerecord --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath file of the gem" do
+ bundle "open activerecord --path lib/version.rb", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/version.rb")
+ end
+
+ it "opens deep subpath of the gem" do
+ bundle "open activerecord --path lib/active_record", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/active_record")
+ end
+
+ it "requires value for --path arg" do
+ bundle "open activerecord --path", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to eq "Cannot specify `--path` option without a value"
+ end
+
+ it "suggests alternatives for similar-sounding gems when using subpath" do
+ bundle "open Rails --path README.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to match(/did you mean rails\?/i)
+ end
+
+ it "suggests alternatives for similar-sounding gems when using deep subpath" do
+ bundle "open Rails --path some/path/here", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ expect(err).to match(/did you mean rails\?/i)
+ end
+
+ it "opens subpath of the short worded gem" do
+ bundle "open rec --path CHANGELOG.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/CHANGELOG.md")
+ end
+
+ it "opens deep subpath of the short worded gem" do
+ bundle "open rec --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path CHANGELOG.md", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/CHANGELOG\.md\z})
+ end
+
+ it "opens deep subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path lib/activerecord/version.rb", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/lib/activerecord/version\.rb\z})
+ end
+
it "select the gem from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
bundle "open active", :env => env do |input, _, _|
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index e084af85d7..bbc3cb54ae 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -192,7 +192,7 @@ RSpec.describe "bundle outdated" do
vcr (6.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
dotenv
diff --git a/spec/bundler/commands/platform_spec.rb b/spec/bundler/commands/platform_spec.rb
index 4e8e3946fe..688bf7b6df 100644
--- a/spec/bundler/commands/platform_spec.rb
+++ b/spec/bundler/commands/platform_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe "bundle platform" do
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
* ruby #{Gem.ruby_version}
@@ -39,7 +39,7 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
* #{Bundler::RubyVersion.system.single_version_string}
@@ -60,7 +60,7 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile does not specify a Ruby version requirement.
G
@@ -80,7 +80,7 @@ G
Your platform is: #{Gem::Platform.local}
Your app has gems that work on these platforms:
-* #{specific_local_platform}
+* #{local_platform}
Your Gemfile specifies a Ruby version requirement:
* ruby #{not_local_ruby_version}
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index 093130f7d5..d757e0be4b 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -522,7 +522,7 @@ RSpec.describe "bundle remove" do
end
end
- context "when gems can not be removed from other gemfile" do
+ context "when gems cannot be removed from other gemfile" do
it "shows error" do
create_file "Gemfile-other", <<-G
gem "rails"; gem "rack"
@@ -574,7 +574,7 @@ RSpec.describe "bundle remove" do
end
context "when gem present in gemfiles but could not be removed from one from one of them" do
- it "removes gem which can be removed and shows warning for file from which it can not be removed" do
+ it "removes gem which can be removed and shows warning for file from which it cannot be removed" do
create_file "Gemfile-other", <<-G
gem "rack"
G
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
index 925e40b71b..1c6244be41 100644
--- a/spec/bundler/commands/show_spec.rb
+++ b/spec/bundler/commands/show_spec.rb
@@ -173,7 +173,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
G
bundle "show rac"
- expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/)
+ expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/)
end
end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 8ca537ac10..84042709bf 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -284,7 +284,7 @@ RSpec.describe "bundle update" do
countries (~> 3.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
countries
@@ -301,6 +301,128 @@ RSpec.describe "bundle update" do
expect(lockfile).to eq(previous_lockfile)
end
+ it "does not downgrade direct dependencies when run with --conservative" do
+ build_repo4 do
+ build_gem "oauth2", "2.0.6" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
+ end
+
+ build_gem "oauth2", "1.4.10" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
+ s.add_dependency "multi_json", "~> 1.3"
+ end
+
+ build_gem "faraday", "2.5.2"
+
+ build_gem "multi_json", "1.15.0"
+
+ build_gem "quickbooks-ruby", "1.0.19" do |s|
+ s.add_dependency "oauth2", "~> 1.4"
+ end
+
+ build_gem "quickbooks-ruby", "0.1.9" do |s|
+ s.add_dependency "oauth2"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "oauth2"
+ gem "quickbooks-ruby"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ faraday (2.5.2)
+ multi_json (1.15.0)
+ oauth2 (1.4.10)
+ faraday (>= 0.17.3, < 3.0)
+ multi_json (~> 1.3)
+ quickbooks-ruby (1.0.19)
+ oauth2 (~> 1.4)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ oauth2
+ quickbooks-ruby
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --conservative --verbose"
+
+ expect(out).not_to include("Installing quickbooks-ruby 0.1.9")
+ expect(out).to include("Installing quickbooks-ruby 1.0.19").and include("Installing oauth2 1.4.10")
+ end
+
+ it "does not downgrade direct dependencies when using gemspec sources" do
+ create_file("rails.gemspec", <<-G)
+ Gem::Specification.new do |gem|
+ gem.name = "rails"
+ gem.version = "7.1.0.alpha"
+ gem.author = "DHH"
+ gem.summary = "Full-stack web application framework."
+ end
+ G
+
+ build_repo4 do
+ build_gem "rake", "12.3.3"
+ build_gem "rake", "13.0.6"
+
+ build_gem "sneakers", "2.11.0" do |s|
+ s.add_dependency "rake"
+ end
+
+ build_gem "sneakers", "2.12.0" do |s|
+ s.add_dependency "rake", "~> 12.3"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec
+
+ gem "rake"
+ gem "sneakers"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: .
+ specs:
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rake (13.0.6)
+ sneakers (2.11.0)
+ rake
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rake
+ sneakers
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --verbose"
+
+ expect(out).not_to include("Installing sneakers 2.12.0")
+ expect(out).not_to include("Installing rake 12.3.3")
+ expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
+ end
+
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|
@@ -536,22 +658,28 @@ RSpec.describe "bundle update" do
bundle "update", :all => true, :raise_on_error => false
expect(last_command).to be_failure
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
- expect(err).to match(/freeze \nby running `bundle config unset deployment`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
+ expect(err).to match(/freeze by running `bundle config set frozen false`./)
end
- it "should suggest different command when frozen is set globally", :bundler => "< 3" do
+ it "should fail loudly when frozen is set globally" do
bundle "config set --global frozen 1"
bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset frozen`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
end
- it "should suggest different command when frozen is set globally", :bundler => "3" do
+ it "should fail loudly when deployment is set globally" do
bundle "config set --global deployment true"
bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset deployment`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
+ end
+
+ it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
+ bundle "update", :all => true, :raise_on_error => false, :env => { "BUNDLE_FROZEN" => "true" }
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
+ expect(err).not_to match(/by running/)
end
end
@@ -644,7 +772,7 @@ RSpec.describe "bundle update" do
end
end
- it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do
+ it "shows the previous version of the gem when updated from rubygems source" do
build_repo2
install_gemfile <<-G
@@ -652,7 +780,7 @@ RSpec.describe "bundle update" do
gem "activesupport"
G
- bundle "update", :all => true
+ bundle "update", :all => true, :verbose => true
expect(out).to include("Using activesupport 2.3.5")
update_repo2 do
@@ -663,32 +791,28 @@ RSpec.describe "bundle update" do
expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
end
- context "with suppress_install_using_messages set" do
- before { bundle "config set suppress_install_using_messages true" }
-
- it "only prints `Using` for versions that have changed" do
- build_repo4 do
- build_gem "bar"
- build_gem "foo"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "bar"
- gem "foo"
- G
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
- bundle "update", :all => true
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bar"
+ gem "foo"
+ G
- update_repo4 do
- build_gem "foo", "2.0"
- end
+ bundle "update", :all => true
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
- bundle "update", :all => true
- out.sub!("Removing foo (1.0)\n", "")
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
+ update_repo4 do
+ build_gem "foo", "2.0"
end
+
+ bundle "update", :all => true
+ out.sub!("Removing foo (1.0)\n", "")
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
end
it "shows error message when Gemfile.lock is not preset and gem is specified" do
@@ -743,7 +867,7 @@ RSpec.describe "bundle update" do
vcr (6.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
dotenv
@@ -1111,7 +1235,7 @@ RSpec.describe "bundle update --ruby" do
end
RSpec.describe "bundle update --bundler" do
- it "updates the bundler version in the lockfile without re-resolving" do
+ it "updates the bundler version in the lockfile" do
build_repo4 do
build_gem "rack", "1.0"
end
@@ -1122,8 +1246,6 @@ RSpec.describe "bundle update --bundler" do
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
- FileUtils.rm_r gem_repo4
-
bundle :update, :bundler => true, :artifice => "compact_index", :verbose => true
expect(out).to include("Using bundler #{Bundler::VERSION}")
@@ -1182,21 +1304,19 @@ RSpec.describe "bundle update --bundler" do
end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do
- skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
-
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
- install_gemfile <<-G
+ install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
- bundle :update, :bundler => true, :artifice => "vcr", :verbose => true
+ bundle :update, :bundler => true, :artifice => "vcr", :verbose => true, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
# Only updates properly on modern RubyGems.
@@ -1228,15 +1348,13 @@ RSpec.describe "bundle update --bundler" do
end
it "errors if the explicit target version does not exist", :realworld do
- skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
-
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
- install_gemfile <<-G
+ install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
@@ -1264,7 +1382,7 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.0.dev"
+ bundle :update, :bundler => "2.3.0.dev", :verbose => "true"
# Only updates properly on modern RubyGems.
@@ -1301,7 +1419,7 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.9", :raise_on_error => false
+ bundle :update, :bundler => "2.3.9", :raise_on_error => false, :verbose => true
expect(out).not_to include("Fetching gem metadata from https://rubygems.org/")
@@ -1327,6 +1445,31 @@ RSpec.describe "bundle update --bundler" do
expect(out).to include("Using bundler 2.3.9")
end
end
+
+ it "prints an error when trying to update bundler in frozen mode" do
+ system_gems "bundler-2.3.9"
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ 2.1.4
+ L
+
+ bundle "update --bundler=2.3.9", :env => { "BUNDLE_FROZEN" => "true" }, :raise_on_error => false
+ expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
+ end
end
# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
@@ -1343,7 +1486,10 @@ RSpec.describe "bundle update conservative" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
@@ -1408,6 +1554,32 @@ RSpec.describe "bundle update conservative" do
expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
end
end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "update --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+
+ it "patch preferred" do
+ bundle "update --patch --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.2.pre", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle "update --minor --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.1.0.pre", "qux 1.0.0"
+ end
+
+ it "major preferred" do
+ bundle "update --major --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+ end
end
context "eager unlocking" do
@@ -1449,7 +1621,7 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
isolated_owner
@@ -1502,7 +1674,7 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
isolated_owner
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
index 0efb24b504..cf612397ab 100644
--- a/spec/bundler/commands/viz_spec.rb
+++ b/spec/bundler/commands/viz_spec.rb
@@ -1,10 +1,8 @@
# frozen_string_literal: true
-RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do
+RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :realworld => true do
before do
- graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
-
- realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
+ realworld_system_gems "ruby-graphviz --version 1.2.5"
end
it "graphs gems from the Gemfile" do
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index 524363fde5..4c6c77a61e 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -53,7 +53,10 @@ RSpec.describe "bundle install with :allow_offline_install" do
File.open(tmp("broken_path/git"), "w", 0o755) do |f|
f.puts strip_whitespace(<<-RUBY)
#!/usr/bin/env ruby
- if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty?
+ fetch_args = %w(fetch --force --quiet)
+ clone_args = %w(clone --bare --no-hardlinks --quiet)
+
+ if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && ARGV.any? {|arg| arg.start_with?("file://") }
warn "git remote ops have been disabled"
exit 1
end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
index 6961171f4f..928ba80b15 100644
--- a/spec/bundler/install/binstubs_spec.rb
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -2,10 +2,8 @@
RSpec.describe "bundle install" do
describe "when system_bindir is set" do
- # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if
- # you want to avoid sudo installs for system gems with OS X's default ruby
it "overrides Gem.bindir" do
- expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0
+ expect(Pathname.new("/usr/bin")).not_to be_writable
gemfile <<-G
def Gem.bindir; "/usr/bin"; end
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index 963ce82db8..e7ec3bc7e7 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -37,12 +37,11 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (= 0.9.1)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.1
+ and Gemfile depends on bundler = 0.9.1,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running.
Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
@@ -58,12 +57,14 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (~> 0.8)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because rails >= 3.0 depends on bundler >= 0.9.0.pre
+ and the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler >= 0.9.0.pre, < 1.A,
+ rails >= 3.0 requires bundler >= 1.A.
+ So, because Gemfile depends on rails = 3.0
+ and Gemfile depends on bundler ~> 0.8,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running.
Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
@@ -79,12 +80,11 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (= 0.9.2)
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.2
+ and Gemfile depends on bundler = 0.9.2,
+ version solving has failed.
Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.
E
@@ -150,13 +150,14 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activemerchant was resolved to 1.0, which depends on
- activesupport (>= 2.0.0)
-
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Could not find compatible versions
+
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and every version of activemerchant depends on activesupport >= 2.0.0,
+ every version of rails_pinned_to_old_activesupport is incompatible with activemerchant >= 0.
+ So, because Gemfile depends on activemerchant >= 0
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
@@ -177,12 +178,13 @@ RSpec.describe "bundle install" do
G
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activesupport (= 2.3.5)
+ Could not find compatible versions
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ activesupport = 1.2.3 is required.
+ So, because Gemfile depends on activesupport = 2.3.5,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
@@ -208,6 +210,33 @@ RSpec.describe "bundle install" do
expect(err).to be_empty
end
+ it "prints the previous version when switching to a previously downloaded gem" do
+ build_repo4 do
+ build_gem "rails", "7.0.3"
+ build_gem "rails", "7.0.4"
+ end
+
+ bundle "config set path.system true"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.4"
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.3"
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'rails', "7.0.4"
+ G
+
+ expect(out).to include("Using rails 7.0.4 (was 7.0.3)")
+ expect(err).to be_empty
+ end
+
it "can install dependencies with newer bundler version with system gems" do
bundle "config set path.system true"
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index 3bcb6a703e..2a88ed5b06 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -39,6 +39,39 @@ RSpec.describe "install in deployment or frozen mode" do
bundle :install
expect(the_bundle).to include_gems "rack 1.0"
end
+
+ it "installs gems by default to vendor/bundle" do
+ bundle :lock
+ bundle "install --deployment"
+ expect(out).to include("vendor/bundle")
+ end
+
+ it "installs gems to custom path if specified" do
+ bundle :lock
+ bundle "install --path vendor/bundle2 --deployment"
+ expect(out).to include("vendor/bundle2")
+ end
+
+ it "works with the --frozen flag" do
+ bundle :lock
+ bundle "install --frozen"
+ end
+
+ it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ bundle :lock
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle "install --deployment", :raise_on_error => false
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have added to the Gemfile")
+ expect(err).to include("* rack-obama")
+ expect(err).not_to include("You have deleted from the Gemfile")
+ expect(err).not_to include("You have changed in the Gemfile")
+ end
end
it "still works if you are not in the app directory and specify --gemfile" do
@@ -141,7 +174,7 @@ RSpec.describe "install in deployment or frozen mode" do
rack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
DEPENDENCIES
rack
@@ -202,29 +235,35 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "install"
end
- it "installs gems by default to vendor/bundle", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems by default to vendor/bundle" do
+ bundle "config set deployment true"
+ bundle "install"
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path if specified", :bundler => "< 3" do
- bundle "install --path vendor/bundle2 --deployment"
+ it "installs gems to custom path if specified" do
+ bundle "config set path vendor/bundle2"
+ bundle "config set deployment true"
+ bundle "install"
expect(out).to include("vendor/bundle2")
end
- it "works with the --deployment flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems to custom path if specified, even when configured through ENV" do
+ bundle "config set deployment true"
+ bundle "install", :env => { "BUNDLE_PATH" => "vendor/bundle2" }
+ expect(out).to include("vendor/bundle2")
end
- it "works with the --frozen flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --frozen"
+ it "works with the `frozen` setting" do
+ bundle "config set frozen true"
+ bundle "install"
end
it "works with BUNDLE_FROZEN if you didn't change anything" do
bundle :install, :env => { "BUNDLE_FROZEN" => "true" }
end
- it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ it "explodes with the `deployment` setting if you make a change and don't check in the lockfile" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -233,7 +272,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -258,8 +297,12 @@ RSpec.describe "install in deployment or frozen mode" do
expect(out).to eq("WIN")
end
- it "works if a gem is missing, but it's on a different platform, and the Gemfile has no global source", :bundler => "< 3" do
+ it "works if a gem is missing, but it's on a different platform" do
+ build_repo2
+
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
source "#{file_uri_for(gem_repo1)}" do
gem "rake", platform: :#{not_local_tag}
end
@@ -269,6 +312,40 @@ RSpec.describe "install in deployment or frozen mode" do
expect(last_command).to be_success
end
+ it "shows a good error if a gem is missing from the lockfile" do
+ build_repo4 do
+ build_gem "foo"
+ build_gem "bar"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install, :env => { "BUNDLE_FROZEN" => "true" }, :raise_on_error => false, :artifice => "compact_index"
+ expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set")
+ end
+
it "explodes if a path gem is missing" do
build_lib "path_gem"
install_gemfile <<-G
@@ -285,7 +362,7 @@ RSpec.describe "install in deployment or frozen mode" do
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
- it "can have --frozen set via an environment variable", :bundler => "< 3" do
+ it "can have --frozen set via an environment variable" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -294,7 +371,7 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_FROZEN"] = "1"
bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -310,20 +387,20 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_DEPLOYMENT"] = "true"
bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have changed in the Gemfile")
end
- it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable", :bundler => "< 3" do
+ it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable" do
ENV["BUNDLE_DEPLOYMENT"] = "true"
bundle "install"
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path when deployment mode is set via an environment variable ", :bundler => "< 3" do
+ it "installs gems to custom path when deployment mode is set via an environment variable " do
ENV["BUNDLE_DEPLOYMENT"] = "true"
ENV["BUNDLE_PATH"] = "vendor/bundle2"
bundle "install"
@@ -340,7 +417,7 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_FROZEN"] = "false"
ENV["BUNDLE_DEPLOYMENT"] = "false"
bundle "install"
- expect(out).not_to include("deployment mode")
+ expect(out).not_to include("frozen mode")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("* rack-obama")
end
@@ -353,7 +430,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
expect(err).to include("You have deleted from the Gemfile:\n* rack")
expect(err).not_to include("You have changed in the Gemfile")
@@ -367,7 +444,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `git://hubz.com`")
end
@@ -387,7 +464,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack-1.0")}` to `no specified source`")
@@ -411,7 +488,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack")}` to `no specified source`")
expect(err).not_to include("You have added to the Gemfile")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -430,7 +507,7 @@ RSpec.describe "install in deployment or frozen mode" do
run "require 'rack'", :raise_on_error => false
expect(err).to include strip_whitespace(<<-E).strip
-The dependencies in your gemfile changed
+The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set (Bundler::ProductionError)
You have added to the Gemfile:
* rack (= 1.0.0)
@@ -463,7 +540,7 @@ You have deleted from the Gemfile:
simulate_new_machine
bundle "config set --local deployment true"
bundle "install --verbose"
- expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("but the lockfile can't be updated because frozen mode is set")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
expect(out).to include("vendor/cache/foo")
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index 941f1c6db9..73f04f071d 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe "bundle install from an existing gemspec" do
output = bundle("install", :dir => tmp.join("foo"))
expect(output).not_to match(/You have added to the Gemfile/)
expect(output).not_to match(/You have deleted from the Gemfile/)
- expect(output).not_to match(/install in deployment mode after changing/)
+ expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/)
end
it "should match a lockfile without needing to re-resolve" do
@@ -436,7 +436,7 @@ RSpec.describe "bundle install from an existing gemspec" do
simulate_new_machine
simulate_platform("jruby") { bundle "install" }
- simulate_platform(x64_mingw) { bundle "install" }
+ simulate_platform(x64_mingw32) { bundle "install" }
end
context "on ruby" do
@@ -674,7 +674,7 @@ RSpec.describe "bundle install from an existing gemspec" do
railties (6.1.4)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
activeadmin!
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index 2058674105..910f96f4ab 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -30,6 +30,13 @@ RSpec.describe "bundle install with git sources" do
expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
end
+ it "does not write to cache on bundler/setup" do
+ cache_path = default_bundle_path.join("cache")
+ FileUtils.rm_rf(cache_path)
+ ruby "require 'bundler/setup'"
+ expect(cache_path).not_to exist
+ end
+
it "caches the git repo globally and properly uses the cached repo on the next invocation" do
simulate_new_machine
bundle "config set global_gem_cache true"
@@ -52,8 +59,9 @@ RSpec.describe "bundle install with git sources" do
bundle "update foo"
sha = git.ref_for("main", 11)
- spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s
- ruby_code = Gem::Specification.load(spec_file).to_ruby
+ spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec")
+ expect(spec_file).to exist
+ ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby
file_code = File.read(spec_file)
expect(file_code).to eq(ruby_code)
end
@@ -192,6 +200,7 @@ RSpec.describe "bundle install with git sources" do
gem "foo"
end
G
+ expect(err).to be_empty
run <<-RUBY
require 'foo'
@@ -218,6 +227,45 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
+ it "works when an abbreviated revision is added after an initial, potentially shallow clone" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision[0..7].inspect} do
+ gem "foo"
+ end
+ G
+ end
+
+ it "works when a tag that does not look like a commit hash is used as the value of :ref" do
+ build_git "foo"
+ @remote = build_git("bar", :bare => true)
+ update_git "foo", :remote => file_uri_for(@remote.path)
+ update_git "foo", :push => "main"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{@remote.path}"
+ G
+
+ # Create a new tag on the remote that needs fetching
+ update_git "foo", :tag => "v1.0.0"
+ update_git "foo", :push => "v1.0.0"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{@remote.path}", :ref => "v1.0.0"
+ G
+
+ expect(err).to be_empty
+ end
+
it "works when the revision is a non-head ref" do
# want to ensure we don't fallback to main
update_git "foo", :path => lib_path("foo-1.0") do |s|
@@ -865,6 +913,9 @@ RSpec.describe "bundle install with git sources" do
end
it "ignores submodules if :submodule is not passed" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -878,12 +929,15 @@ RSpec.describe "bundle install with git sources" do
gem "has_submodule"
end
G
- expect(err).to match(/could not find gem 'submodule/i)
+ expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally})
expect(the_bundle).not_to include_gems "has_submodule 1.0"
end
it "handles repos with submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -902,6 +956,9 @@ RSpec.describe "bundle install with git sources" do
end
it "does not warn when deiniting submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0"
@@ -1094,6 +1151,17 @@ RSpec.describe "bundle install with git sources" do
G
expect(err).to include("Revision deadbeef does not exist in the repository")
end
+
+ it "gives a helpful error message when the remote branch no longer exists" do
+ build_git "foo"
+
+ install_gemfile <<-G, :env => { "LANG" => "en" }, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef"
+ G
+
+ expect(err).to include("Revision deadbeef does not exist in the repository")
+ end
end
describe "bundle install with deployment mode configured and git sources" do
@@ -1211,7 +1279,7 @@ RSpec.describe "bundle install with git sources" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
create_makefile("foo")
RUBY
s.write "ext/foo.c", "void Init_foo() {}"
@@ -1431,8 +1499,6 @@ In Gemfile:
describe "without git installed" do
it "prints a better error message when installing" do
- build_git "foo"
-
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 515901064f..a5207036c3 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -92,14 +92,12 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "demo", :path => lib_path("demo")
build_lib "aaa", :path => lib_path("demo/aaa")
- gemfile = <<-G
+ gemfile lib_path("demo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
gem "aaa", :path => "./aaa"
G
- File.open(lib_path("demo/Gemfile"), "w") {|f| f.puts gemfile }
-
lockfile = <<~L
PATH
remote: .
@@ -314,18 +312,67 @@ RSpec.describe "bundle install with explicit source paths" do
s.add_dependency "rack", "1.0"
end
- gemfile = <<-G
+ gemfile lib_path("foo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
G
- File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
-
bundle "install", :dir => lib_path("foo")
expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
end
+ it "does not unlock dependencies of path sources" do
+ build_repo4 do
+ build_gem "graphql", "2.0.15"
+ build_gem "graphql", "2.0.16"
+ end
+
+ build_lib "foo", "0.1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ gemfile_path = lib_path("foo/Gemfile")
+
+ gemfile gemfile_path, <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec
+ G
+
+ lockfile_path = lib_path("foo/Gemfile.lock")
+
+ original_lockfile = <<~L
+ PATH
+ remote: .
+ specs:
+ foo (0.1.0)
+ graphql (~> 2.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ graphql (2.0.15)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_path, original_lockfile
+
+ build_lib "foo", "0.1.1", :path => lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ bundle "install", :dir => lib_path("foo")
+ expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
+ end
+
it "supports gemspec syntax with an alternative path" do
build_lib "foo", "1.0", :path => lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
@@ -735,6 +782,52 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
end
+
+ it "does not remove existing ruby platform" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "0.9.1"
+ end
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 0.9.1)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
end
describe "switching sources" do
@@ -791,13 +884,11 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when there are both a gemspec and remote gems" do
it "doesn't query rubygems for local gemspec name" do
build_lib "private_lib", "2.2", :path => lib_path("private_lib")
- gemfile = <<-G
+ gemfile lib_path("private_lib/Gemfile"), <<-G
source "http://localgemserver.test"
gemspec
gem 'rack'
G
- File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
-
bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib")
expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
expect(out).not_to match(/^HTTP GET.*private_lib/)
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index a357a92272..219ae6c2f4 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -75,6 +75,81 @@ RSpec.describe "bundle install across platforms" do
expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
end
+ context "on universal Rubies" do
+ before do
+ build_repo4 do
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "ruby"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 RUBY'"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "arm64-darwin"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 arm64-darwin'"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "x86_64-darwin"
+ s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 x86_64-darwin'"
+ end
+ end
+ end
+
+ it "pulls in the correct architecture gem" do
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21"
+ simulate_ruby_platform "universal.x86_64-darwin21" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 x86_64-darwin"
+ end
+ end
+
+ it "pulls in the correct architecture gem on arm64e macOS Ruby" do
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21"
+ simulate_ruby_platform "universal.arm64e-darwin21" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 arm64-darwin"
+ end
+ end
+ end
+
it "works with gems that have different dependencies" do
simulate_platform "java"
install_gemfile <<-G
@@ -384,7 +459,7 @@ RSpec.describe "bundle install with platform conditionals" do
tzinfo (2.0.4)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -475,7 +550,7 @@ RSpec.describe "bundle install with platform conditionals" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ gem "rack", :platform => [:windows, :mingw, :mswin, :x64_mingw, :jruby]
G
bundle "install"
@@ -497,11 +572,31 @@ RSpec.describe "bundle install with platform conditionals" do
#{Bundler::VERSION}
L
end
+
+ it "resolves fine when a dependency is unused on a platform different from the current one, but reintroduced transitively" do
+ bundle "config set --local force_ruby_platform true"
+
+ build_repo4 do
+ build_gem "listen", "3.7.1" do |s|
+ s.add_dependency "ffi"
+ end
+
+ build_gem "ffi", "1.15.5"
+ end
+
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "listen"
+ gem "ffi", :platform => :windows
+ G
+ expect(err).to be_empty
+ end
end
RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index ee0aa2a347..dd86187a6b 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "fails", :bundler => "3" do
- bundle :instal, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -262,7 +262,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
depends_on_rack!
@@ -371,7 +371,15 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "fails" do
bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Could not find gem 'missing', which is required by gem 'depends_on_missing', in any of the sources.")
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of depends_on_missing depends on missing >= 0
+ and missing >= 0 could not be found in any of the sources,
+ depends_on_missing cannot be used.
+ So, because Gemfile depends on depends_on_missing >= 0,
+ version solving has failed.
+ E
end
end
@@ -425,9 +433,15 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "does not find the dependency" do
bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include(
- "Could not find gem 'rack', which is required by gem 'depends_on_rack', in rubygems repository https://gem.repo2/ or installed locally."
- )
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of depends_on_rack depends on rack >= 0
+ and rack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally,
+ depends_on_rack cannot be used.
+ So, because Gemfile depends on depends_on_rack >= 0,
+ version solving has failed.
+ E
end
end
@@ -626,7 +640,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
zeitwerk (2.4.2)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -682,7 +696,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -766,7 +780,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -822,7 +836,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
activesupport
@@ -910,7 +924,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
nokogiri (>= 1.2.3)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
handsoap!
@@ -970,7 +984,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1000,7 +1014,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1022,7 +1036,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
rack!
@@ -1306,7 +1320,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(out).to include("Using example 0.1.0")
end
- it "fails inmmediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do
+ it "fails immediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do
gemfile <<-G
source "https://gem.repo1"
@@ -1325,7 +1339,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(err).to include("Could not find gem 'example' in locally installed gems.")
end
- it "fails inmmediately with a helpful error when a non retriable network error happens while resolving sources" do
+ it "fails immediately with a helpful error when a non retriable network error happens while resolving sources" do
gemfile <<-G
source "https://gem.repo1"
@@ -1434,7 +1448,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.3.1)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1458,7 +1472,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.0.0)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1470,6 +1484,59 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
+ context "when default source includes old gems with nil required_ruby_version" do
+ before do
+ build_repo2 do
+ build_gem "ruport", "1.7.0.3" do |s|
+ s.add_dependency "pdf-writer", "1.1.8"
+ end
+ end
+
+ build_repo gem_repo4 do
+ build_gem "pdf-writer", "1.1.8"
+ end
+
+ path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
+ spec.instance_variable_set(:@required_ruby_version, nil)
+ File.open(path, "wb") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ gemfile <<~G
+ source "https://localgemserver.test"
+
+ gem "ruport", "= 1.7.0.3", :source => "https://localgemserver.test/extra"
+ G
+ end
+
+ it "handles that fine" do
+ bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ pdf-writer (1.1.8)
+
+ GEM
+ remote: https://localgemserver.test/extra/
+ specs:
+ ruport (1.7.0.3)
+ pdf-writer (= 1.1.8)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ ruport (= 1.7.0.3)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
context "when default source includes old gems with nil required_rubygems_version" do
before do
build_repo2 do
@@ -1512,7 +1579,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
ruport (= 1.7.0.3)!
@@ -1553,7 +1620,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (1.1.8)
PLATFORMS
- #{specific_local_platform}
+ #{local_platform}
DEPENDENCIES
pdf-writer (= 1.1.8)
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index bb5526203f..cab53a663a 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -6,10 +6,8 @@ RSpec.describe "bundle install with specific platforms" do
gem "google-protobuf"
G
- context "when on a darwin machine" do
- before { simulate_platform "x86_64-darwin-15" }
-
- it "locks to the specific darwin platform" do
+ it "locks to the specific darwin platform" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
@@ -19,8 +17,10 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
])
end
+ end
- it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -53,8 +53,10 @@ RSpec.describe "bundle install with specific platforms" do
# make sure the platform that got actually installed with the old bundler is used
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
end
+ end
- it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -103,8 +105,56 @@ RSpec.describe "bundle install with specific platforms" do
#{Bundler::VERSION}
L
end
+ end
+
+ context "when running on a legacy lockfile locked only to RUBY" do
+ around do |example|
+ build_repo4 do
+ build_gem "nokogiri", "1.3.10"
+ build_gem "nokogiri", "1.3.10" do |s|
+ s.platform = "arm64-darwin"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
- it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.3.10)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-22", &example
+ end
+
+ it "still installs the generic RUBY variant if necessary" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(out).to include("Installing nokogiri 1.3.10")
+ end
+
+ it "still installs the generic RUBY variant if necessary, even in frozen mode" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" }
+ expect(out).to include("Installing nokogiri 1.3.10")
+ end
+ end
+
+ it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ simulate_platform "x86_64-darwin-15" do
build_repo2 do
build_gem("libv8", "8.4.255.0")
build_gem("libv8", "8.4.255.0") {|s| s.platform = "universal-darwin" }
@@ -147,8 +197,44 @@ RSpec.describe "bundle install with specific platforms" do
bundle "add mini_racer --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)")
end
+ end
+
+ it "chooses platform specific gems even when resolving upon materialization and the API returns more specific platforms first" do
+ simulate_platform "x86_64-darwin-15" do
+ build_repo4 do
+ build_gem("grpc", "1.50.0")
+ build_gem("grpc", "1.50.0") {|s| s.platform = "universal-darwin" }
+ end
- it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
+ gemfile <<-G
+ source "https://localgemserver.test"
+ gem "grpc"
+ G
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ grpc (1.50.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ grpc
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", :artifice => "compact_index_precompiled_before", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(out).to include("Installing grpc 1.50.0 (universal-darwin)")
+ end
+ end
+
+ it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
gemfile(google_protobuf)
bundle "cache --all-platforms"
@@ -157,8 +243,10 @@ RSpec.describe "bundle install with specific platforms" do
bundle "install --verbose"
expect(err).to be_empty
end
+ end
- it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do
+ it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
gemfile(google_protobuf)
bundle "config set --local cache_all_platforms true"
@@ -168,44 +256,46 @@ RSpec.describe "bundle install with specific platforms" do
bundle "install --verbose"
expect(err).to be_empty
end
+ end
- it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do
- git = build_git "pg_array_parser", "1.0"
+ it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do
+ git = build_git "pg_array_parser", "1.0"
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
- G
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
+ G
- lockfile <<-L
- GIT
- remote: #{lib_path("pg_array_parser-1.0")}
- revision: #{git.ref_for("main")}
- specs:
- pg_array_parser (1.0-java)
- pg_array_parser (1.0)
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("pg_array_parser-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ pg_array_parser (1.0-java)
+ pg_array_parser (1.0)
- GEM
- specs:
+ GEM
+ specs:
- PLATFORMS
- java
- #{lockfile_platforms}
+ PLATFORMS
+ java
+ #{lockfile_platforms}
- DEPENDENCIES
- pg_array_parser!
+ DEPENDENCIES
+ pg_array_parser!
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
- bundle "config set --local cache_all true"
- bundle "cache --all-platforms"
+ bundle "config set --local cache_all true"
+ bundle "cache --all-platforms"
- expect(err).to be_empty
- end
+ expect(err).to be_empty
+ end
- it "uses the platform-specific gem with extra dependencies" do
+ it "uses the platform-specific gem with extra dependencies" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem_with_different_dependencies_per_platform
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -218,25 +308,29 @@ RSpec.describe "bundle install with specific platforms" do
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
"facter-2.4.6-universal-darwin"])
end
+ end
- context "when adding a platform via lock --add_platform" do
- before do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
+ context "when adding a platform via lock --add_platform" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ end
- it "adds the foreign platform" do
+ it "adds the foreign platform" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{x64_mingw}"
+ bundle "lock --add-platform=#{x64_mingw32}"
- expect(the_bundle.locked_gems.platforms).to eq([x64_mingw, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to eq([x64_mingw32, pl("x86_64-darwin-15")])
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
])
end
+ end
- it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ it "falls back on plain ruby when that version doesn't have a platform-specific gem" do
+ simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{java}"
@@ -331,7 +425,13 @@ RSpec.describe "bundle install with specific platforms" do
G
error_message = <<~ERROR.strip
- Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21', which is required by gem 'sorbet (= 0.5.6433)', in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+ Could not find compatible versions
+
+ Because every version of sorbet depends on sorbet-static = 0.5.6433
+ and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally for any resolution platforms (arm64-darwin-21),
+ sorbet cannot be used.
+ So, because Gemfile depends on sorbet = 0.5.6433,
+ version solving has failed.
The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
* sorbet-static-0.5.6433-universal-darwin-20
@@ -411,7 +511,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms_for([specific_local_platform, "ruby"])}
+ #{lockfile_platforms("ruby")}
DEPENDENCIES
sorbet-static-and-runtime
@@ -445,6 +545,203 @@ RSpec.describe "bundle install with specific platforms" do
L
end
+ it "automatically fixes the lockfile if both RUBY platform and a more specific platform are locked, and some gem has no RUBY variant available" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem "nokogiri", "1.13.0"
+ build_gem "nokogiri", "1.13.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem("sorbet-static", "0.5.10601") do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "update --conservative nokogiri"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.13.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile if only RUBY platform is locked and some gem has no RUBY variant available" do
+ build_repo4 do
+ build_gem("sorbet-static-and-runtime", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet", "= 0.5.10160"
+ s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160"
+ end
+
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_runtime_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-runtime", "0.5.10160")
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "sorbet-static-and-runtime"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile without removing other variants if it's missing platform gems, but they are installed locally" do
+ simulate_platform "x86_64-darwin-21" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-20"
+ end
+
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ # Make sure sorbet-static-0.5.10549-universal-darwin-21 is installed
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "sorbet-static", "= 0.5.10549"
+ G
+
+ # Make sure the lockfile is missing sorbet-static-0.5.10549-universal-darwin-21
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+ sorbet-static (0.5.10549-universal-darwin-21)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
it "does not remove ruby if gems for other platforms, and not present in the lockfile, exist in the Gemfile" do
build_repo4 do
build_gem "nokogiri", "1.13.8"
@@ -469,7 +766,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri (1.13.8-#{Gem::Platform.local})
PLATFORMS
- #{lockfile_platforms_for([specific_local_platform, "ruby"])}
+ #{lockfile_platforms("ruby")}
DEPENDENCIES
nokogiri
@@ -486,6 +783,56 @@ RSpec.describe "bundle install with specific platforms" do
expect(lockfile).to eq(original_lockfile)
end
+ it "does not remove ruby when adding a new gem to the Gemfile" do
+ build_repo4 do
+ build_gem "concurrent-ruby", "1.2.2"
+ build_gem "rack", "3.0.7"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "concurrent-ruby"
+ gem "rack"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ concurrent-ruby (1.2.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ concurrent-ruby
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ concurrent-ruby (1.2.2)
+ rack (3.0.7)
+
+ PLATFORMS
+ #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+
+ DEPENDENCIES
+ concurrent-ruby
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
it "can fallback to a source gem when platform gems are incompatible with current ruby version" do
setup_multiplatform_gem_with_source_gem
@@ -498,7 +845,7 @@ RSpec.describe "bundle install with specific platforms" do
G
# simulate lockfile which includes both a precompiled gem with:
- # - Gem the current platform (with imcompatible ruby version)
+ # - Gem the current platform (with incompatible ruby version)
# - A source gem with compatible ruby version
lockfile <<-L
GEM
@@ -521,6 +868,64 @@ RSpec.describe "bundle install with specific platforms" do
bundle :install
end
+ it "automatically fixes the lockfile if the specific platform is locked and we move to a newer ruby version for which a native package is not available" do
+ #
+ # Given an existing application using native gems (e.g., nokogiri)
+ # And a lockfile generated with a stable ruby version
+ # When want test the application against ruby-head and `bundle install`
+ # Then bundler should fall back to the generic ruby platform gem
+ #
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri", "1.14.0"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
private
def setup_multiplatform_gem
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index 5c25b1092a..65be262748 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -406,7 +406,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps" do
+ it "does not fetch every spec when doing back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -416,9 +416,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "compact_index_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -428,7 +426,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps & everything is the compact index" do
+ it "does not fetch every spec when doing back deps & everything is the compact index" do
build_repo4 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -438,9 +436,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo4)
-
- install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -519,18 +515,6 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "compact_index"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
it "installs the binstubs", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
@@ -699,6 +683,15 @@ The checksum of /versions does not match the checksum provided by the server! So
bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
expect(err).to include("Bad username or password")
end
+
+ it "does not fallback to old dependency API if bad authentication is provided" do
+ bundle "config set #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false, :verbose => true
+ expect(err).to include("Bad username or password")
+ expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions")
+ expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies")
+ end
end
describe "with no password" do
@@ -942,7 +935,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
expect(err).to include(<<-E.strip)
Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}).
-Either installing with `--full-index` or running `bundle update rails` should fix the problem.
+Running `bundle update rails` should fix the problem.
E
end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index 79317a7fad..a54f1db772 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -119,7 +119,7 @@ RSpec.describe "gemcutter's dependency API" do
end
it "falls back when the API errors out" do
- simulate_platform mswin
+ simulate_platform x86_mswin32
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
@@ -359,7 +359,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 3" do
+ it "does not fetch every spec when doing back deps", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -369,9 +369,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
@@ -380,7 +378,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do
+ it "does not fetch every spec when doing back deps using blocks" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -390,9 +388,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- api_request_limit = low_api_request_limit_for(gem_repo2)
-
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -443,6 +439,22 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
+ it "does not fetch all marshaled specs" do
+ build_repo2 do
+ build_gem "foo", "1.0"
+ build_gem "foo", "2.0"
+ end
+
+ install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :verbose => true
+ source "#{source_uri}"
+
+ gem "foo"
+ G
+
+ expect(out).to include("foo-2.0.gemspec.rz")
+ expect(out).not_to include("foo-1.0.gemspec.rz")
+ end
+
it "does not refetch if the only unmet dependency is bundler" do
build_repo2 do
build_gem "bundler_dep" do |s|
@@ -460,18 +472,6 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "endpoint"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
it "installs the binstubs", :bundler => "< 3" do
gemfile <<-G
source "#{source_uri}"
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index ed61531574..d5fa55be48 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -190,28 +190,35 @@ RSpec.describe "bundle flex_install" do
expect(err).to match(/could not find gem 'rack-obama/i)
end
- it "suggests deleting the Gemfile.lock file when the Gemfile requires different versions than the lock" do
+ it "discards the locked gems when the Gemfile requires different versions than the lock" do
bundle "config set force_ruby_platform true"
nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "rack":
- In snapshot (Gemfile.lock):
- rack (= 0.9.1)
+ Could not find compatible versions
- In Gemfile:
- rack-obama (= 2.0) was resolved to 2.0, which depends on
- rack (= 1.2)
-
- rack_middleware was resolved to 1.0, which depends on
- rack (= 0.9.1)
-
- Deleting your Gemfile.lock file and running `bundle install` will rebuild your snapshot from scratch, using only
- the gems in your Gemfile, which may resolve the conflict.
+ Because rack-obama >= 2.0 depends on rack = 1.2
+ and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally,
+ rack-obama >= 2.0 cannot be used.
+ So, because Gemfile depends on rack-obama = 2.0,
+ version solving has failed.
E
bundle :install, :retry => 0, :raise_on_error => false
expect(err).to end_with(nice_error)
end
+
+ it "does not include conflicts with a single requirement tree, because that can't possibly be a conflict" do
+ bundle "config set force_ruby_platform true"
+
+ bad_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "rack-obama":
+ In Gemfile:
+ rack-obama (= 2.0)
+ E
+
+ bundle "update rack_middleware", :retry => 0, :raise_on_error => false
+ expect(err).not_to end_with(bad_error)
+ end
end
describe "when running bundle update and Gemfile conflicts with lockfile" do
@@ -230,22 +237,6 @@ RSpec.describe "bundle flex_install" do
gem "jekyll-feed", "~> 0.12"
G
- lockfile <<-L
- GEM
- remote: #{file_uri_for(gem_repo4)}/
- specs:
- jekyll-feed (0.16.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- jekyll-feed
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "github-pages", "~> 226"
@@ -253,24 +244,9 @@ RSpec.describe "bundle flex_install" do
G
end
- it "suggests deleting the Gemfile.lock file when the Gemfile requires different versions than the lock" do
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "jekyll-feed":
- In snapshot (Gemfile.lock):
- jekyll-feed (>= 0.16.0)
-
- In Gemfile:
- jekyll-feed (~> 0.12)
-
- github-pages (~> 226) was resolved to 226, which depends on
- jekyll-feed (= 0.15.1)
-
- Deleting your Gemfile.lock file and running `bundle install` will rebuild your snapshot from scratch, using only
- the gems in your Gemfile, which may resolve the conflict.
- E
-
- bundle :update, :raise_on_error => false
- expect(err).to end_with(nice_error)
+ it "discards the conflicting lockfile information and resolves properly" do
+ bundle :update, :raise_on_error => false, :all => true
+ expect(err).to be_empty
end
end
@@ -374,7 +350,7 @@ RSpec.describe "bundle flex_install" do
end
end
- it "prints the correct error message" do
+ it "resolves them" do
# install Rails 3.0.0.rc
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -383,13 +359,12 @@ RSpec.describe "bundle flex_install" do
G
# upgrade Rails to 3.0.0 and then install again
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "rails", "3.0.0"
gem "capybara", "0.3.9"
G
-
- expect(err).to include("Gemfile.lock")
+ expect(err).to be_empty
end
end
end
diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb
index 436454c1f4..9aadc9ed25 100644
--- a/spec/bundler/install/gems/fund_spec.rb
+++ b/spec/bundler/install/gems/fund_spec.rb
@@ -6,20 +6,20 @@ RSpec.describe "bundle install" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index c3e05586bd..5c18d2cc51 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -52,7 +52,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -97,7 +97,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle_#{n}"
dir_config(name)
raise "OMG" unless with_config("c_extension_#{n}") == "#{n}"
@@ -150,7 +150,7 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index 9c0d6bfe56..fb6dda2f88 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -159,7 +159,7 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -173,7 +173,7 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -187,12 +187,10 @@ RSpec.describe "bundle install with install-time dependencies" do
bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
- activated_groups = "net_b (1.0) (ruby), net_b (1.0) (#{specific_local_platform})"
-
expect(out).to include(" net_b").
- and include("BUNDLER: Starting resolution").
- and include("BUNDLER: Finished resolution").
- and include("Attempting to activate [#{activated_groups}]")
+ and include("Resolving dependencies...").
+ and include("Solution found after 1 attempts:").
+ and include("selected net_b 1.0")
end
end
end
@@ -305,15 +303,137 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
+ context "with transitive dependencies in a lockfile" do
+ before do
+ build_repo2 do
+ build_gem "rubocop", "1.28.2" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.17.0", "< 2.0"
+ end
+
+ build_gem "rubocop", "1.35.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.20.1", "< 2.0"
+ end
+
+ build_gem "rubocop-ast", "1.17.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "rubocop-ast", "1.21.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
+ end
+
+ gemfile <<-G
+ source "http://localgemserver.test/"
+ gem 'rubocop'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.35.0)
+ rubocop-ast (>= 1.20.1, < 2.0)
+ rubocop-ast (1.21.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older compatible versions" do
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.28.2)
+ rubocop-ast (>= 1.17.0, < 2.0)
+ rubocop-ast (1.17.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rubocop
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with a Gemfile and lock file that don't resolve under the current platform" do
+ before do
+ build_repo4 do
+ build_gem "sorbet", "0.5.10554" do |s|
+ s.add_dependency "sorbet-static", "0.5.10554"
+ end
+
+ build_gem "sorbet-static", "0.5.10554" do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'sorbet', '= 0.5.10554'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10554)
+ sorbet-static (= 0.5.10554)
+ sorbet-static (0.5.10554-universal-darwin-21)
+
+ PLATFORMS
+ arm64-darwin-21
+
+ DEPENDENCIES
+ sorbet (= 0.5.10554)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a proper error" do
+ simulate_platform "aarch64-linux" do
+ bundle "install", :raise_on_error => false
+ end
+
+ nice_error = strip_whitespace(<<-E).strip
+ Could not find gem 'sorbet-static (= 0.5.10554)' with platforms 'arm64-darwin-21', 'aarch64-linux' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
+ * sorbet-static-0.5.10554-universal-darwin-21
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
it "gives a meaningful error on ruby version mismatches between dependencies" do
build_repo4 do
build_gem "requires-old-ruby" do |s|
- s.required_ruby_version = "< #{RUBY_VERSION}"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
end
end
build_lib("foo", :path => bundled_app) do |s|
- s.required_ruby_version = ">= #{RUBY_VERSION}"
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
s.add_dependency "requires-old-ruby"
end
@@ -323,7 +443,16 @@ RSpec.describe "bundle install with install-time dependencies" do
gemspec
G
- expect(err).to include("Bundler found conflicting requirements for the Ruby\0 version:")
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of foo depends on requires-old-ruby >= 0
+ and every version of requires-old-ruby depends on Ruby < #{Gem.ruby_version},
+ every version of foo requires Ruby < #{Gem.ruby_version}.
+ So, because Gemfile depends on foo >= 0
+ and current Ruby version is = #{Gem.ruby_version},
+ version solving has failed.
+ E
end
it "installs the older version under rate limiting conditions" do
@@ -352,13 +481,13 @@ RSpec.describe "bundle install with install-time dependencies" do
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2" do |s|
- s.platform = mingw
+ s.platform = x86_mingw32
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2"
end
- simulate_platform mingw do
+ simulate_platform x86_mingw32 do
install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
@@ -393,14 +522,13 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- require_ruby was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
-
- Current Ruby\0 version:
- Ruby\0 (#{error_message_requirement})
+ Could not find compatible versions
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -416,14 +544,13 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- require_ruby was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
-
- Current Ruby\0 version:
- Ruby\0 (#{error_message_requirement})
+ Could not find compatible versions
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -461,14 +588,11 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(err).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the RubyGems\0 version:
- In Gemfile:
- require_rubygems was resolved to 1.0, which depends on
- RubyGems\0 (> 9000)
-
- Current RubyGems\0 version:
- RubyGems\0 (= #{Gem::VERSION})
-
+ Because every version of require_rubygems depends on RubyGems > 9000
+ and Gemfile depends on require_rubygems >= 0,
+ RubyGems > 9000 is required.
+ So, because current RubyGems version is = #{Gem::VERSION},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index 238006c02b..4d08752256 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -128,11 +128,6 @@ RSpec.shared_examples "bundle install --standalone" do
skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
- if Gem.win_platform? && RUBY_VERSION < "3.1.0"
- default_fiddle_version = ruby "require 'fiddle'; puts Gem.loaded_specs['fiddle'].version"
- realworld_system_gems "fiddle --version #{default_fiddle_version}"
- end
-
realworld_system_gems "tsort --version 0.1.0"
necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"]
@@ -162,7 +157,7 @@ RSpec.shared_examples "bundle install --standalone" do
bundle "lock", :dir => cwd, :artifice => "compact_index"
end
- it "works and points to the vendored copies, not to the default copies" do
+ it "works and points to the vendored copies, not to the default copies", :realworld do
bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd, :artifice => "compact_index", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
@@ -175,7 +170,7 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
- describe "with Gemfiles using path sources and resulting bundle moved to a folder hierarchy with different nesting" do
+ describe "with Gemfiles using absolute path sources and resulting bundle moved to a folder hierarchy with different nesting" do
before do
build_lib "minitest", "1.0.0", :path => lib_path("minitest")
@@ -205,6 +200,35 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
+ describe "with Gemfiles using relative path sources and app moved to a different root" do
+ before do
+ FileUtils.mkdir_p bundled_app("app/vendor")
+
+ build_lib "minitest", "1.0.0", :path => bundled_app("app/vendor/minitest")
+
+ gemfile bundled_app("app/Gemfile"), <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "minitest", :path => "vendor/minitest"
+ G
+
+ bundle "install", :standalone => true, :dir => bundled_app("app")
+
+ FileUtils.mv(bundled_app("app"), bundled_app2("app"))
+ end
+
+ it "also works" do
+ ruby <<-RUBY, :dir => bundled_app2("app")
+ require "./bundle/bundler/setup"
+
+ require "minitest"
+ puts MINITEST
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+ end
+
describe "with gems with native extension" do
before do
bundle "config set --local path #{bundled_app("bundle")}"
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
deleted file mode 100644
index 41b241da25..0000000000
--- a/spec/bundler/install/gems/sudo_spec.rb
+++ /dev/null
@@ -1,205 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "when using sudo", :sudo => true do
- describe "and BUNDLE_PATH is writable" do
- context "but BUNDLE_PATH/build_info is not writable" do
- let(:subdir) do
- system_gem_path("cache")
- end
-
- before do
- bundle "config set path.system true"
- subdir.mkpath
- sudo "chmod u-w #{subdir}"
- end
-
- after do
- sudo "chmod u+w #{subdir}"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- expect(out).to_not match(/an error occurred/i)
- expect(system_gem_path("cache/rack-1.0.0.gem")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
- end
- end
-
- describe "and GEM_HOME is owned by root" do
- before :each do
- bundle "config set path.system true"
- chown_system_gems_to_root
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- gem "thin"
- G
-
- expect(system_gem_path("gems/rack-1.0.0")).to exist
- expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs rake and a gem dependent on rake in the same session" do
- build_repo2 do
- build_gem "another_implicit_rake_dep" do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- path = File.expand_path("lib", __dir__)
- FileUtils.mkdir_p(path)
- File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
- f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
- end
- end
- RUBY
- end
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rake"
- gem "another_implicit_rake_dep"
- G
- bundle "install"
- expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist
- end
-
- it "installs when BUNDLE_PATH is owned by root" do
- bundle_path = tmp("owned_by_root")
- FileUtils.mkdir_p bundle_path
- sudo "chown -R root #{bundle_path}"
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs when BUNDLE_PATH does not exist" do
- root_path = tmp("owned_by_root")
- FileUtils.mkdir_p root_path
- sudo "chown -R root #{root_path}"
- bundle_path = root_path.join("does_not_exist")
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs extensions/" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "very_simple_binary"
- G
-
- expect(system_gem_path("gems/very_simple_binary-1.0")).to exist
- binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0")
- expect(Dir.glob(binary_glob).first).to be
- end
- end
-
- describe "and BUNDLE_PATH is not writable" do
- before do
- bundle "config set --local path .bundle"
- sudo "chmod ugo-w .bundle"
- end
-
- after do
- sudo "chmod ugo+w .bundle"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(local_gem_path("gems/rack-1.0.0")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "cleans up the tmpdirs generated" do
- require "tmpdir"
- Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir|
- FileUtils.remove_entry_secure(tmpdir)
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*")
-
- expect(tmpdirs).to be_empty
- end
- end
-
- describe "and GEM_HOME is not writable" do
- it "installs" do
- bundle "config set path.system true"
- gem_home = tmp("sudo_gem_home")
- sudo "mkdir -p #{gem_home}"
- sudo "chmod ugo-w #{gem_home}"
-
- system_gems :bundler, :path => gem_home
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
- expect(gem_home.join("bin/rackup")).to exist
- expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
-
- sudo "rm -rf #{tmp("sudo_gem_home")}"
- end
- end
-
- describe "and root runs install" do
- let(:warning) { "Don't run Bundler as root." }
-
- before do
- gemfile %(source "#{file_uri_for(gem_repo1)}")
- end
-
- it "warns against that" do
- bundle :install, :sudo => :preserve_env
- expect(err).to include(warning)
- end
-
- context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do
- it "skips the warning" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "true" }
- expect(err).to_not include(warning)
- end
- end
-
- context "when silence_root_warning = false" do
- it "warns against that" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" }
- expect(err).to include(warning)
- end
- end
- end
-end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index 0fa7ed8531..882f2a2d42 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -98,5 +98,77 @@ RSpec.describe "bundle install" do
bundle "info zebra"
expect(out).to include("* zebra (2.0 #{revision_for(lib_path("gems"))[0..6]})")
end
+
+ it "should always sort dependencies in the same order" do
+ # This Gemfile + lockfile had a problem where the first
+ # `bundle install` would change the order, but the second would
+ # change it back.
+
+ # NOTE: both gems MUST have the same path! It has to be two gems in one repo.
+
+ test = build_git "test", "1.0.0", :path => lib_path("test-and-other")
+ other = build_git "other", "1.0.0", :path => lib_path("test-and-other")
+ test_ref = test.ref_for("HEAD")
+ other_ref = other.ref_for("HEAD")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
+ gem "test", git: #{test.path.to_s.inspect}
+ gem "other", ref: #{other_ref.inspect}, git: #{other.path.to_s.inspect}
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{test.path}
+ revision: #{test_ref}
+ specs:
+ test (1.0.0)
+
+ GIT
+ remote: #{other.path}
+ revision: #{other_ref}
+ ref: #{other_ref}
+ specs:
+ other (1.0.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ other!
+ test!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # If GH#6743 is present, the first `bundle install` will change the
+ # lockfile, by flipping the order (`other` would be moved to the top).
+ #
+ # The second `bundle install` would then change the lockfile back
+ # to the original.
+ #
+ # The fix makes it so it may change it once, but it will not change
+ # it a second time.
+ #
+ # So, we run `bundle install` once, and store the value of the
+ # modified lockfile.
+ bundle :install
+ modified_lockfile = lockfile
+
+ # If GH#6743 is present, the second `bundle install` would change the
+ # lockfile back to what it was originally.
+ #
+ # This `expect` makes sure it doesn't change a second time.
+ bundle :install
+ expect(lockfile).to eq(modified_lockfile)
+
+ expect(out).to include("Bundle complete!")
+ end
end
end
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index 3021d6ae17..be34d8b5bc 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -220,9 +220,9 @@ RSpec.describe "global gem caching" do
gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}"
G
- gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ gem_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0")
- git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ git_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
"very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0")
cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 09a5ba0be1..dc054b50bb 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -30,6 +30,72 @@ RSpec.context "when installing a bundle that includes yanked gems" do
expect(err).to include("Your bundle is locked to foo (10.0.0)")
end
+ context "when a re-resolve is necessary, and a yanked version is considered by the resolver" do
+ before do
+ skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform?
+
+ build_repo4 do
+ build_gem "foo", "1.0.0", "1.0.1"
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.6" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "nokogiri", "1.13.8"
+ end
+
+ gemfile <<~G
+ source "#{source_uri}"
+ gem "foo", "1.0.1"
+ gem "actiontext", "6.1.6"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{source_uri}/
+ specs:
+ actiontext (6.1.6)
+ nokogiri (>= 1.8)
+ foo (1.0.0)
+ nokogiri (1.13.8-#{Bundler.local_platform})
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ actiontext (= 6.1.6)
+ foo (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "and the old index is used" do
+ let(:source_uri) { file_uri_for(gem_repo4) }
+
+ it "reports the yanked gem properly" do
+ bundle "install", :raise_on_error => false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+ end
+
+ context "and the compact index API is used" do
+ let(:source_uri) { "https://gem.repo4" }
+
+ it "reports the yanked gem properly" do
+ bundle "install", :artifice => "compact_index", :raise_on_error => false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+ end
+ end
+
it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
bundle "config set force_ruby_platform true"
@@ -126,6 +192,12 @@ RSpec.context "when using gem before installing" do
expect(err).to_not include("Your bundle is locked to rack (0.9.1) from")
expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+
+ # Check error message is still correct when multiple platforms are locked
+ lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle :list, :raise_on_error => false
+ expect(err).to include("Could not find rack-0.9.1 in locally installed gems")
end
it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
index 56db5d8305..ac3d10223c 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -14,6 +14,52 @@ RSpec.describe "bundle lock with git gems" do
expect(the_bundle).to include_gems "foo 1.0.0"
end
+ it "doesn't print errors even if running lock after removing the cache" do
+ FileUtils.rm_rf(Dir[default_cache_path("git/foo-1.0-*")].first)
+
+ bundle "lock --verbose"
+
+ expect(err).to be_empty
+ end
+
+ it "prints a proper error when changing a locked Gemfile to point to a bad branch" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}", :branch => "bad"
+ G
+
+ bundle "lock --update foo", :env => { "LANG" => "en" }, :raise_on_error => false
+
+ expect(err).to include("Revision bad does not exist in the repository")
+ end
+
+ it "prints a proper error when installing a Gemfile with a locked ref that does not exist" do
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{"a" * 40}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", :raise_on_error => false
+
+ expect(err).to include("Revision #{"a" * 40} does not exist in the repository")
+ end
+
it "locks a git source to the current ref" do
update_git "foo"
bundle :install
@@ -26,6 +72,87 @@ RSpec.describe "bundle lock with git gems" do
expect(out).to eq("WIN")
end
+ it "properly clones a git source locked to an out of date ref" do
+ update_git "foo"
+
+ bundle :install, :env => { "BUNDLE_PATH" => "foo" }
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an unreachable ref" do
+ # Create a commit and make it unreachable
+ git "checkout -b foo ", lib_path("foo-1.0")
+ unreachable_sha = update_git("foo").ref_for("HEAD")
+ git "checkout main ", lib_path("foo-1.0")
+ git "branch -D foo ", lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{unreachable_sha}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an annotated tag" do
+ # Create an annotated tag
+ git("tag -a v1.0 -m 'Annotated v1.0'", lib_path("foo-1.0"))
+ annotated_tag = git("rev-parse v1.0", lib_path("foo-1.0"))
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{annotated_tag}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
it "provides correct #full_gem_path" do
run <<-RUBY
puts Bundler.rubygems.find_name('foo').first.full_gem_path
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index 29f8863591..ccf23a9e3c 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -540,6 +540,92 @@ RSpec.describe "the lockfile format" do
G
end
+ it "is conservative with dependencies of git gems" do
+ build_repo4 do
+ build_gem "orm_adapter", "0.4.1"
+ build_gem "orm_adapter", "0.5.0"
+ end
+
+ FileUtils.mkdir_p lib_path("ckeditor/lib")
+
+ @remote = build_git("ckeditor_remote", :bare => true)
+
+ build_git "ckeditor", :path => lib_path("ckeditor") do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.7'"
+ s.version = "4.0.7"
+ s.add_dependency "orm_adapter"
+ end
+
+ update_git "ckeditor", :path => lib_path("ckeditor"), :remote => file_uri_for(@remote.path)
+ update_git "ckeditor", :path => lib_path("ckeditor"), :tag => "v4.0.7"
+ old_git = update_git "ckeditor", :path => lib_path("ckeditor"), :push => "v4.0.7"
+
+ update_git "ckeditor", :path => lib_path("ckeditor"), :gemspec => true do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.8'"
+ s.version = "4.0.8"
+ s.add_dependency "orm_adapter"
+ end
+ update_git "ckeditor", :path => lib_path("ckeditor"), :tag => "v4.0.8"
+
+ new_git = update_git "ckeditor", :path => lib_path("ckeditor"), :push => "v4.0.8"
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "ckeditor", :git => "#{@remote.path}", :tag => "v4.0.8"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{old_git.ref_for("v4.0.7")}
+ tag: v4.0.7
+ specs:
+ ckeditor (4.0.7)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ # Bumps the git gem, but keeps its dependency locked
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{new_git.ref_for("v4.0.8")}
+ tag: v4.0.8
+ specs:
+ ckeditor (4.0.8)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
it "serializes pinned path sources to the lockfile" do
build_lib "foo"
@@ -982,7 +1068,7 @@ RSpec.describe "the lockfile format" do
rack (1.0.0)
PLATFORMS
- #{lockfile_platforms_for(["java", specific_local_platform])}
+ #{lockfile_platforms("java")}
DEPENDENCIES
rack
@@ -1218,7 +1304,178 @@ RSpec.describe "the lockfile format" do
G
expect(err).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")}).").
- and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
+ and include("Running `bundle update rack_middleware` should fix the problem.")
+ end
+
+ it "regenerates a lockfile with no specs" do
+ build_repo4 do
+ build_gem "indirect_dependency", "1.2.3" do |s|
+ s.metadata["funding_uri"] = "https://example.com/donate"
+ end
+
+ build_gem "direct_dependency", "4.5.6" do |s|
+ s.add_dependency "indirect_dependency", ">= 0"
+ end
+ end
+
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ direct_dependency
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "direct_dependency"
+ G
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ direct_dependency (4.5.6)
+ indirect_dependency
+ indirect_dependency (1.2.3)
+
+ PLATFORMS
+ #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+
+ DEPENDENCIES
+ direct_dependency
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ shared_examples_for "a lockfile missing dependent specs" do
+ it "auto-heals" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "minitest-bisect"
+ G
+
+ # Corrupt lockfile (completely missing path_expander)
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
+ bundle :install
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with just specific platform" do
+ let(:platforms) { lockfile_platforms }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ context "with both ruby and specific platform" do
+ let(:platforms) { lockfile_platforms("ruby") }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ it "auto-heals when the lockfile is missing specs" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "minitest-bisect"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ expect(out).to include("re-resolving dependencies because your lock file is missing \"minitest-bisect\"")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
describe "a line ending" do
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index e14b5ff003..7b7ceb4586 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -587,10 +587,9 @@ RSpec.describe "major deprecations" do
pending "fails with a helpful message", :bundler => "3"
end
- context "bundle viz" do
+ context "bundle viz", :realworld do
before do
- graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
- realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
+ realworld_system_gems "ruby-graphviz --version 1.2.5"
create_file "gems.rb", "source \"#{file_uri_for(gem_repo1)}\""
bundle "viz"
end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index 009516260a..aec131f2ee 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -22,6 +22,13 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo")
end
+ it "installs from rubygems source in frozen mode" do
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)}", :env => { "BUNDLE_DEPLOYMENT" => "true" }
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
it "installs from sources configured as Gem.sources without any flags" do
bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
@@ -32,7 +39,8 @@ RSpec.describe "bundler plugin install" do
it "shows help when --help flag is given" do
bundle "plugin install --help"
- expect(out).to include("bundle plugin install PLUGINS # Install the plugin from the source")
+ # The help message defined in ../../lib/bundler/man/bundle-plugin.1.ronn will be output.
+ expect(out).to include("You can install, uninstall, and list plugin(s)")
end
context "plugin is already installed" do
@@ -84,6 +92,26 @@ RSpec.describe "bundler plugin install" do
expect(out).to include("Using foo 1.1")
end
+ it "installs when --branch specified" do
+ bundle "plugin install foo --branch main --source #{file_uri_for(gem_repo2)}"
+
+ expect(out).to include("Installed plugin foo")
+ end
+
+ it "installs when --ref specified" do
+ bundle "plugin install foo --ref v1.2.3 --source #{file_uri_for(gem_repo2)}"
+
+ expect(out).to include("Installed plugin foo")
+ end
+
+ it "raises error when both --branch and --ref options are specified" do
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --branch main --ref v1.2.3", :raise_on_error => false
+
+ expect(out).not_to include("Installed plugin foo")
+
+ expect(err).to include("You cannot specify `--branch` and `--ref` at the same time.")
+ end
+
it "works with different load paths" do
build_repo2 do
build_plugin "testing" do |s|
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 412de04d44..9d153b6063 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -304,13 +304,7 @@ RSpec.describe "real source plugins" do
@install_path ||= begin
git_scope = "\#{base_name}-\#{shortref_for_path(revision)}"
- path = gem_install_dir.join(git_scope)
-
- if !path.exist? && requires_sudo?
- user_bundle_path.join(ruby_scope).join(git_scope)
- else
- path
- end
+ gem_install_dir.join(git_scope)
end
end
diff --git a/spec/bundler/quality_es_spec.rb b/spec/bundler/quality_es_spec.rb
index 90968e6270..0dbd77e451 100644
--- a/spec/bundler/quality_es_spec.rb
+++ b/spec/bundler/quality_es_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe "La biblioteca si misma" do
included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
+ next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -52,7 +52,7 @@ RSpec.describe "La biblioteca si misma" do
error_messages = []
exempt = /vendor/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index ee98bcd4d7..a98815158e 100644
--- a/spec/bundler/quality_spec.rb
+++ b/spec/bundler/quality_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe "The library itself" do
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ merge_conflicts_regex
+ failing_lines << number + 1 if line&.match?(merge_conflicts_regex)
end
return if failing_lines.empty?
@@ -32,8 +32,8 @@ RSpec.describe "The library itself" do
def check_for_extra_spaces(filename)
failing_lines = []
each_line(filename) do |line, number|
- next if line =~ /^\s+#.*\s+\n$/
- failing_lines << number + 1 if line =~ /\s+\n$/
+ next if /^\s+#.*\s+\n$/.match?(line)
+ failing_lines << number + 1 if /\s+\n$/.match?(line)
end
return if failing_lines.empty?
@@ -45,7 +45,7 @@ RSpec.describe "The library itself" do
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ /’/
+ failing_lines << number + 1 if /’/.match?(line)
end
return if failing_lines.empty?
@@ -89,7 +89,7 @@ RSpec.describe "The library itself" do
exempt = /\.gitmodules|fixtures|vendor|LICENSE|vcr_cassettes|rbreadline\.diff|index\.txt$/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_tab_characters(filename)
error_messages << check_for_extra_spaces(filename)
end
@@ -100,7 +100,7 @@ RSpec.describe "The library itself" do
exempt = /vendor|vcr_cassettes|LICENSE|rbreadline\.diff/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_straneous_quotes(filename)
end
expect(error_messages.compact).to be_well_formed
@@ -110,7 +110,7 @@ RSpec.describe "The library itself" do
error_messages = []
exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_git_merge_conflicts(filename)
end
expect(error_messages.compact).to be_well_formed
@@ -120,7 +120,7 @@ RSpec.describe "The library itself" do
included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
+ next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -131,7 +131,7 @@ RSpec.describe "The library itself" do
error_messages = []
exempt = /vendor|vcr_cassettes|CODE_OF_CONDUCT/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -151,7 +151,6 @@ RSpec.describe "The library itself" do
git.allow_insecure
inline
trust-policy
- use_gem_version_promoter_for_major_updates
]
all_settings = Hash.new {|h, k| h[k] = [] }
@@ -198,7 +197,7 @@ RSpec.describe "The library itself" do
gem_list = loaded_gemspec.files
- expect(git_list.sort).to eq(gem_list.sort)
+ expect(git_list).to match_array(gem_list)
end
it "does not contain any warnings" do
@@ -207,7 +206,6 @@ RSpec.describe "The library itself" do
lib/bundler/deployment.rb
lib/bundler/gem_tasks.rb
lib/bundler/vlad.rb
- lib/bundler/templates/gems.rb
]
files_to_require = lib_tracked_files.grep(/\.rb$/) - exclusions
files_to_require.reject! {|f| f.start_with?("lib/bundler/vendor") }
@@ -230,7 +228,7 @@ RSpec.describe "The library itself" do
exempt = %r{templates/|\.5|\.1|vendor/}
all_bad_requires = []
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
each_line(filename) do |line, number|
line.scan(/^ *require "bundler/).each { all_bad_requires << "#{filename}:#{number.succ}" }
end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
index 08c6acf190..14f99bd262 100644
--- a/spec/bundler/realworld/dependency_api_spec.rb
+++ b/spec/bundler/realworld/dependency_api_spec.rb
@@ -13,12 +13,12 @@ RSpec.describe "gemcutter's dependency API", :realworld => true do
require_relative "../support/artifice/endpoint_timeout"
@t = Thread.new do
- server = Rack::Server.start(:app => EndpointTimeout,
- :Host => "0.0.0.0",
- :Port => port,
- :server => "webrick",
+ server = Rack::Server.start(:app => EndpointTimeout,
+ :Host => "0.0.0.0",
+ :Port => port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
server.start
end
@t.run
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
index 2a667011a1..2f465b7b25 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -4,14 +4,15 @@ RSpec.describe "real world edgecases", :realworld => true do
def rubygems_version(name, requirement)
ruby <<-RUBY
require "#{spec_dir}/support/artifice/vcr"
- require "#{entrypoint}"
- require "#{entrypoint}/source/rubygems/remote"
- require "#{entrypoint}/fetcher"
+ require "bundler"
+ require "bundler/source/rubygems/remote"
+ require "bundler/fetcher"
rubygem = Bundler.ui.silence do
source = Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org"))
fetcher = Bundler::Fetcher.new(source)
index = fetcher.specs([#{name.dump}], nil)
- index.search(Gem::Dependency.new(#{name.dump}, #{requirement.dump})).last
+ requirement = Gem::Requirement.create(#{requirement.dump})
+ index.search(#{name.dump}).select {|spec| requirement.satisfied_by?(spec.version) }.last
end
if rubygem.nil?
raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
@@ -64,7 +65,7 @@ RSpec.describe "real world edgecases", :realworld => true do
it "is able to update a top-level dependency when there is a conflict on a shared transitive child" do
# from https://github.com/rubygems/bundler/issues/5031
- system_gems "bundler-2.99.0"
+ pristine_system_gems "bundler-1.99.0"
gemfile <<-G
source "https://rubygems.org"
@@ -154,7 +155,7 @@ RSpec.describe "real world edgecases", :realworld => true do
activemodel (= 4.2.7.1)
activerecord (= 4.2.7.1)
activesupport (= 4.2.7.1)
- bundler (>= 1.3.0, < 3.0)
+ bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7.1)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
@@ -191,7 +192,7 @@ RSpec.describe "real world edgecases", :realworld => true do
rails (~> 4.2.7.1)
L
- bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "2.99.0" }
+ bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "1.99.0" }
expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end
@@ -218,14 +219,14 @@ RSpec.describe "real world edgecases", :realworld => true do
end
it "doesn't hang on big gemfile" do
- skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM.include?("darwin")
+ skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
# frozen_string_literal: true
source "https://rubygems.org"
- ruby "2.7.3"
+ ruby "~> 2.7.7"
gem "rails"
gem "pg", ">= 0.18", "< 2.0"
@@ -320,17 +321,17 @@ RSpec.describe "real world edgecases", :realworld => true do
G
if Bundler.feature_flag.bundler_3_mode?
- # Conflicts on bundler version, so fails earlier
+ # Conflicts on bundler version, so we count attempts differently
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }, :raise_on_error => false
- expect(out).to display_total_steps_of(435)
+ expect(out.split("\n").grep(/backtracking to/).count).to eq(16)
else
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- expect(out).to display_total_steps_of(1025)
+ expect(out).to include("Solution found after 7 attempts")
end
end
it "doesn't hang on tricky gemfile" do
- skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM.include?("darwin")
+ skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
source 'https://rubygems.org'
@@ -348,190 +349,168 @@ RSpec.describe "real world edgecases", :realworld => true do
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- if Bundler.feature_flag.bundler_3_mode?
- expect(out).to display_total_steps_of(890)
- else
- expect(out).to display_total_steps_of(891)
- end
+ expect(out).to include("Solution found after 6 attempts")
end
it "doesn't hang on nix gemfile" do
- skip "Only for ruby 3.0.1" if RUBY_VERSION != "3.0.1" || RUBY_PLATFORM.include?("darwin")
+ skip "Only for ruby 3.0" unless RUBY_VERSION.start_with?("3.0")
gemfile <<~G
- source "https://rubygems.org" do
- gem "addressable"
- gem "atk"
- gem "awesome_print"
- gem "bacon"
- gem "byebug"
- gem "cairo"
- gem "cairo-gobject"
- gem "camping"
- gem "charlock_holmes"
- gem "cld3"
- gem "cocoapods"
- gem "cocoapods-acknowledgements"
- gem "cocoapods-art"
- gem "cocoapods-bin"
- gem "cocoapods-browser"
- gem "cocoapods-bugsnag"
- gem "cocoapods-check"
- gem "cocoapods-clean"
- gem "cocoapods-clean_build_phases_scripts"
- gem "cocoapods-core"
- gem "cocoapods-coverage"
- gem "cocoapods-deintegrate"
- gem "cocoapods-dependencies"
- gem "cocoapods-deploy"
- gem "cocoapods-downloader"
- gem "cocoapods-expert-difficulty"
- gem "cocoapods-fix-react-native"
- gem "cocoapods-generate"
- gem "cocoapods-git_url_rewriter"
- gem "cocoapods-keys"
- gem "cocoapods-no-dev-schemes"
- gem "cocoapods-open"
- gem "cocoapods-packager"
- gem "cocoapods-playgrounds"
- gem "cocoapods-plugins"
- gem "cocoapods-prune-localizations"
- gem "cocoapods-rome"
- gem "cocoapods-search"
- gem "cocoapods-sorted-search"
- gem "cocoapods-static-swift-framework"
- gem "cocoapods-stats"
- gem "cocoapods-tdfire-binary"
- gem "cocoapods-testing"
- gem "cocoapods-trunk"
- gem "cocoapods-try"
- gem "cocoapods-try-release-fix"
- gem "cocoapods-update-if-you-dare"
- gem "cocoapods-whitelist"
- gem "cocoapods-wholemodule"
- gem "coderay"
- gem "concurrent-ruby"
- gem "curb"
- gem "curses"
- gem "daemons"
- gem "dep-selector-libgecode"
- gem "digest-sha3"
- gem "domain_name"
- gem "do_sqlite3"
- gem "ethon"
- gem "eventmachine"
- gem "excon"
- gem "faraday"
- gem "ffi"
- gem "ffi-rzmq-core"
- gem "fog-dnsimple"
- gem "gdk_pixbuf2"
- gem "gio2"
- gem "gitlab-markup"
- gem "glib2"
- gem "gpgme"
- gem "gtk2"
- gem "hashie"
- gem "highline"
- gem "hike"
- gem "hitimes"
- gem "hpricot"
- gem "httpclient"
- gem "http-cookie"
- gem "iconv"
- gem "idn-ruby"
- gem "jbuilder"
- gem "jekyll"
- gem "jmespath"
- gem "jwt"
- gem "libv8"
- gem "libxml-ruby"
- gem "magic"
- gem "markaby"
- gem "method_source"
- gem "mini_magick"
- gem "msgpack"
- gem "mysql2"
- gem "ncursesw"
- gem "netrc"
- gem "net-scp"
- gem "net-ssh"
- gem "nokogiri"
- gem "opus-ruby"
- gem "ovirt-engine-sdk"
- gem "pango"
- gem "patron"
- gem "pcaprub"
- gem "pg"
- gem "pry"
- gem "pry-byebug"
- gem "pry-doc"
- gem "public_suffix"
- gem "puma"
- gem "rails"
- gem "rainbow"
- gem "rbnacl"
- gem "rb-readline"
- gem "re2"
- gem "redis"
- gem "redis-rack"
- gem "rest-client"
- gem "rmagick"
- gem "rpam2"
- gem "rspec"
- gem "rubocop"
- gem "rubocop-performance"
- gem "ruby-libvirt"
- gem "ruby-lxc"
- gem "ruby-progressbar"
- gem "ruby-terminfo"
- gem "ruby-vips"
- gem "rubyzip"
- gem "rugged"
- gem "sassc"
- gem "scrypt"
- gem "semian"
- gem "sequel"
- gem "sequel_pg"
- gem "simplecov"
- gem "sinatra"
- gem "slop"
- gem "snappy"
- gem "sqlite3"
- gem "taglib-ruby"
- gem "thrift"
- gem "tilt"
- gem "tiny_tds"
- gem "treetop"
- gem "typhoeus"
- gem "tzinfo"
- gem "unf_ext"
- gem "uuid4r"
- gem "whois"
- gem "zookeeper"
- end
+ source "https://rubygems.org"
+
+ gem "addressable"
+ gem "atk"
+ gem "awesome_print"
+ gem "bacon"
+ gem "byebug"
+ gem "cairo"
+ gem "cairo-gobject"
+ gem "camping"
+ gem "charlock_holmes"
+ gem "cld3"
+ gem "cocoapods"
+ gem "cocoapods-acknowledgements"
+ gem "cocoapods-art"
+ gem "cocoapods-bin"
+ gem "cocoapods-browser"
+ gem "cocoapods-bugsnag"
+ gem "cocoapods-check"
+ gem "cocoapods-clean"
+ gem "cocoapods-clean_build_phases_scripts"
+ gem "cocoapods-core"
+ gem "cocoapods-coverage"
+ gem "cocoapods-deintegrate"
+ gem "cocoapods-dependencies"
+ gem "cocoapods-deploy"
+ gem "cocoapods-downloader"
+ gem "cocoapods-expert-difficulty"
+ gem "cocoapods-fix-react-native"
+ gem "cocoapods-generate"
+ gem "cocoapods-git_url_rewriter"
+ gem "cocoapods-keys"
+ gem "cocoapods-no-dev-schemes"
+ gem "cocoapods-open"
+ gem "cocoapods-packager"
+ gem "cocoapods-playgrounds"
+ gem "cocoapods-plugins"
+ gem "cocoapods-prune-localizations"
+ gem "cocoapods-rome"
+ gem "cocoapods-search"
+ gem "cocoapods-sorted-search"
+ gem "cocoapods-static-swift-framework"
+ gem "cocoapods-stats"
+ gem "cocoapods-tdfire-binary"
+ gem "cocoapods-testing"
+ gem "cocoapods-trunk"
+ gem "cocoapods-try"
+ gem "cocoapods-try-release-fix"
+ gem "cocoapods-update-if-you-dare"
+ gem "cocoapods-whitelist"
+ gem "cocoapods-wholemodule"
+ gem "coderay"
+ gem "concurrent-ruby"
+ gem "curb"
+ gem "curses"
+ gem "daemons"
+ gem "dep-selector-libgecode"
+ gem "digest-sha3"
+ gem "domain_name"
+ gem "do_sqlite3"
+ gem "ethon"
+ gem "eventmachine"
+ gem "excon"
+ gem "faraday"
+ gem "ffi"
+ gem "ffi-rzmq-core"
+ gem "fog-dnsimple"
+ gem "gdk_pixbuf2"
+ gem "gio2"
+ gem "gitlab-markup"
+ gem "glib2"
+ gem "gpgme"
+ gem "gtk2"
+ gem "hashie"
+ gem "highline"
+ gem "hike"
+ gem "hitimes"
+ gem "hpricot"
+ gem "httpclient"
+ gem "http-cookie"
+ gem "iconv"
+ gem "idn-ruby"
+ gem "jbuilder"
+ gem "jekyll"
+ gem "jmespath"
+ gem "jwt"
+ gem "libv8"
+ gem "libxml-ruby"
+ gem "magic"
+ gem "markaby"
+ gem "method_source"
+ gem "mini_magick"
+ gem "msgpack"
+ gem "mysql2"
+ gem "ncursesw"
+ gem "netrc"
+ gem "net-scp"
+ gem "net-ssh"
+ gem "nokogiri"
+ gem "opus-ruby"
+ gem "ovirt-engine-sdk"
+ gem "pango"
+ gem "patron"
+ gem "pcaprub"
+ gem "pg"
+ gem "pry"
+ gem "pry-byebug"
+ gem "pry-doc"
+ gem "public_suffix"
+ gem "puma"
+ gem "rails"
+ gem "rainbow"
+ gem "rbnacl"
+ gem "rb-readline"
+ gem "re2"
+ gem "redis"
+ gem "redis-rack"
+ gem "rest-client"
+ gem "rmagick"
+ gem "rpam2"
+ gem "rspec"
+ gem "rubocop"
+ gem "rubocop-performance"
+ gem "ruby-libvirt"
+ gem "ruby-lxc"
+ gem "ruby-progressbar"
+ gem "ruby-terminfo"
+ gem "ruby-vips"
+ gem "rubyzip"
+ gem "rugged"
+ gem "sassc"
+ gem "scrypt"
+ gem "semian"
+ gem "sequel"
+ gem "sequel_pg"
+ gem "simplecov"
+ gem "sinatra"
+ gem "slop"
+ gem "snappy"
+ gem "sqlite3"
+ gem "taglib-ruby"
+ gem "thrift"
+ gem "tilt"
+ gem "tiny_tds"
+ gem "treetop"
+ gem "typhoeus"
+ gem "tzinfo"
+ gem "unf_ext"
+ gem "uuid4r"
+ gem "whois"
+ gem "zookeeper"
G
bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- if Bundler.feature_flag.bundler_3_mode?
- expect(out).to display_total_steps_of(1874)
- else
- expect(out).to display_total_steps_of(1922)
- end
- end
-
- private
-
- RSpec::Matchers.define :display_total_steps_of do |expected_steps|
- match do |out|
- out.include?("BUNDLER: Finished resolution (#{expected_steps} steps)")
- end
-
- failure_message do |out|
- actual_steps = out.scan(/BUNDLER: Finished resolution \((\d+) steps\)/).first.first
-
- "Expected resolution to finish in #{expected_steps} steps, but took #{actual_steps}"
- end
+ expect(out).to include("Solution found after 4 attempts")
end
end
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
index 083ea38901..fdefc14091 100644
--- a/spec/bundler/realworld/ffi_spec.rb
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "loading dinamically linked library on a bundle exec context", :realworld => true do
+RSpec.describe "loading dynamically linked library on a bundle exec context", :realworld => true do
it "passes ENV right after argv in memory" do
create_file "foo.rb", <<~RUBY
require 'ffi'
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
index ada2fc92ee..60c0055a62 100644
--- a/spec/bundler/realworld/gemfile_source_header_spec.rb
+++ b/spec/bundler/realworld/gemfile_source_header_spec.rb
@@ -40,12 +40,12 @@ RSpec.describe "fetching dependencies with a mirrored source", :realworld => tru
require_relative "../support/artifice/endpoint_mirror_source"
@t = Thread.new do
- Rack::Server.start(:app => EndpointMirrorSource,
- :Host => "0.0.0.0",
- :Port => @port,
- :server => "webrick",
+ Rack::Server.start(:app => EndpointMirrorSource,
+ :Host => "0.0.0.0",
+ :Port => @port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
end.run
wait_for_server("127.0.0.1", @port)
diff --git a/spec/bundler/realworld/git_spec.rb b/spec/bundler/realworld/git_spec.rb
new file mode 100644
index 0000000000..3d352626ea
--- /dev/null
+++ b/spec/bundler/realworld/git_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.describe "github source", :realworld => true do
+ it "properly fetches PRs" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "reline", github: "https://github.com/ruby/reline/pull/488"
+ G
+ end
+end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
index 241424d4d6..f2ce477c10 100644
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -113,12 +113,12 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
require_relative "../support/artifice/endpoint"
@server_thread = Thread.new do
- Rack::Server.start(:app => Endpoint,
- :Host => host,
- :Port => @server_port,
- :server => "webrick",
+ Rack::Server.start(:app => Endpoint,
+ :Host => host,
+ :Port => @server_port,
+ :server => "webrick",
:AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ :Logger => Spec::SilentLogger.new)
end.run
wait_for_server(host, @server_port)
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
index 9f11821bf5..aa8a48fcc7 100644
--- a/spec/bundler/realworld/slow_perf_spec.rb
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -11,12 +11,23 @@ RSpec.describe "bundle install with complex dependencies", :realworld => true do
gem "mongoid", ">= 0.10.2"
G
- start_time = Time.now
+ expect { bundle "lock" }.to take_less_than(18) # seconds
+ end
+
+ it "resolves quickly (case 2)" do
+ gemfile <<-G
+ source "https://rubygems.org"
- bundle "lock"
+ gem 'metasploit-erd'
+ gem 'rails-erd'
+ gem 'yard'
- duration = Time.now - start_time
+ gem 'coveralls'
+ gem 'rails'
+ gem 'simplecov'
+ gem 'rspec-rails'
+ G
- expect(duration.to_f).to be < 18 # seconds
+ expect { bundle "lock" }.to take_less_than(30) # seconds
end
end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
index ee62dc3577..f739f8c02b 100644
--- a/spec/bundler/resolver/basic_spec.rb
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe "Resolving" do
dep "chef_app_error"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict)
+ end.to raise_error(Bundler::SolveFailure)
end
it "raises an exception with the minimal set of conflicting dependencies" do
@@ -118,14 +118,15 @@ RSpec.describe "Resolving" do
dep "c"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict, <<-E.strip)
-Bundler could not find compatible versions for gem "a":
- In Gemfile:
- b was resolved to 1.0, which depends on
- a (>= 2)
-
- c was resolved to 1.0, which depends on
- a (< 1)
+ end.to raise_error(Bundler::SolveFailure, <<~E.strip)
+ Could not find compatible versions
+
+ Because every version of c depends on a < 1
+ and every version of b depends on a >= 2,
+ every version of c is incompatible with b >= 0.
+ So, because Gemfile depends on b >= 0
+ and Gemfile depends on c >= 0,
+ version solving has failed.
E
end
@@ -134,7 +135,7 @@ Bundler could not find compatible versions for gem "a":
dep "circular_app"
expect do
- resolve
+ Bundler::SpecSet.new(resolve).sort
end.to raise_error(Bundler::CyclicDependencyError, /please remove either gem 'bar' or gem 'foo'/i)
end
@@ -174,12 +175,7 @@ Bundler could not find compatible versions for gem "a":
dep "foo"
dep "Ruby\0", "1.8.7"
- deps = []
- @deps.each do |d|
- deps << Bundler::DepProxy.get_proxy(d, "ruby")
- end
-
- should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]]
+ should_resolve_and_include %w[foo-1.0.0 bar-1.0.0]
end
context "conservative" do
@@ -305,4 +301,50 @@ Bundler could not find compatible versions for gem "a":
end
end
end
+
+ it "handles versions that redundantly depend on themselves" do
+ @index = build_index do
+ gem "rack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "rack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 0"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "rack", ">= 0"
+ end
+ end
+
+ dep "rack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[rack-3.0.0 standalone_migrations-2.0.4]
+ end
+
+ it "ignores versions that incorrectly depend on themselves" do
+ @index = build_index do
+ gem "rack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "rack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 2.0.5"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "rack", ">= 0"
+ end
+ end
+
+ dep "rack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[rack-3.0.0 standalone_migrations-1.0.13]
+ end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index 8eaed4220a..a710dfcb28 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -82,21 +82,105 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[foo-1.0.0-x64-mingw32]
end
- it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version" do
- @index = build_index do
- gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
- gem "foo", "1.1.0"
- gem "foo", "1.1.0", "x64-mingw32" do |s|
- s.required_ruby_version = [">= 2.0", "< 2.4"]
+ describe "on a linux platform", :rubygems => ">= 3.1.0.pre.1" do
+ # Ruby's platform is *-linux => platform's libc is glibc, so not musl
+ # Ruby's platform is *-linux-musl => platform's libc is musl, so not glibc
+ # Gem's platform is *-linux => gem is glibc + maybe musl compatible
+ # Gem's platform is *-linux-musl => gem is musl compatible but not glibc
+
+ it "favors the platform version-specific gem on a version-specifying linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
end
- gem "Ruby\0", "2.5.1"
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux-musl]
end
- dep "foo"
- dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
- should_resolve_as %w[foo-1.1.0]
+ it "favors the version-less gem over the version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "ignores the platform version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_not_resolve
+ end
+
+ it "falls back to the platform version-less gem on a linux platform with a version" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "falls back to the ruby platform gem on a gnu linux platform when only a version-specifying gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0]
+ end
+
+ it "falls back to the platform version-less gem on a version-specifying linux platform and no ruby platform gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+ end
+
+ context "when the platform specific gem doesn't match the required_ruby_version" do
+ before do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.1.0"
+ gem "foo", "1.1.0", "x64-mingw32" do |s|
+ s.required_ruby_version = [">= 2.0", "< 2.4"]
+ end
+ gem "Ruby\0", "2.5.1"
+ end
+ dep "Ruby\0", "2.5.1"
+ platforms "x64-mingw32"
+ end
+
+ it "takes the latest ruby gem" do
+ dep "foo"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
+
+ it "takes the latest ruby gem, even if requirement does not match previous versions with the same ruby requirement" do
+ dep "foo", "1.1.0"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
end
it "takes the latest ruby gem with required_ruby_version if the platform specific gem doesn't match the required_ruby_version" do
@@ -137,39 +221,6 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[foo-1.1.0]
end
- it "doesn't include gems not needed for none of the platforms" do
- @index = build_index do
- gem "empyrean", "0.1.0"
- gem "coderay", "1.1.2"
- gem "method_source", "0.9.0"
-
- gem "spoon", "0.0.6" do
- dep "ffi", ">= 0"
- end
-
- gem "pry", "0.11.3", "java" do
- dep "coderay", "~> 1.1.0"
- dep "method_source", "~> 0.9.0"
- dep "spoon", "~> 0.0"
- end
-
- gem "pry", "0.11.3" do
- dep "coderay", "~> 1.1.0"
- dep "method_source", "~> 0.9.0"
- end
-
- gem "ffi", "1.9.23", "java"
- gem "ffi", "1.9.23"
- end
-
- dep "empyrean", "0.1.0"
- dep "pry"
-
- platforms "ruby", "java"
-
- should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6]
- end
-
it "includes gems needed for at least one platform" do
@index = build_index do
gem "empyrean", "0.1.0"
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index e3cf5020ec..29ef036828 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -96,12 +96,14 @@ RSpec.describe "bundler/inline#gemfile" do
it "lets me use my own ui object" do
script <<-RUBY, :artifice => "endpoint"
require '#{entrypoint}'
- class MyBundlerUI < Bundler::UI::Silent
+ class MyBundlerUI < Bundler::UI::Shell
def confirm(msg, newline = nil)
puts "CONFIRMED!"
end
end
- gemfile(true, :ui => MyBundlerUI.new) do
+ my_ui = MyBundlerUI.new
+ my_ui.level = "confirm"
+ gemfile(true, :ui => my_ui) do
source "https://notaserver.com"
gem "activesupport", :require => true
end
@@ -166,6 +168,54 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "installs subdependencies quietly if necessary when the install option is not set" do
+ build_repo4 do
+ build_gem "rack" do |s|
+ s.add_dependency "rackdep"
+ end
+
+ build_gem "rackdep", "1.0.0"
+ end
+
+ script <<-RUBY
+ gemfile do
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rack"
+ end
+
+ require "rackdep"
+ puts RACKDEP
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs subdependencies quietly if necessary when the install option is not set, and multiple sources used" do
+ build_repo4 do
+ build_gem "rack" do |s|
+ s.add_dependency "rackdep"
+ end
+
+ build_gem "rackdep", "1.0.0"
+ end
+
+ script <<-RUBY
+ gemfile do
+ source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo4)}" do
+ gem "rack"
+ end
+ end
+
+ require "rackdep"
+ puts RACKDEP
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
it "installs quietly from git if necessary when the install option is not set" do
build_git "foo", "1.0.0"
baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
@@ -205,6 +255,113 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "doesn't reinstall already installed gems" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "doesn't reinstall already installed gems in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "activesupport"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing rack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems with native extensions in later gemfile calls" do
+ system_gems "rack-1.0.0"
+
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ script <<-RUBY
+ require '#{entrypoint}'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+
+ require 'foo'
+ puts FOO
+ puts $:.grep(/ext/)
+ RUBY
+
+ expect(out).to include("YES")
+ expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ expect(err).to be_empty
+ end
+
it "installs inline gems when a Gemfile.lock is present" do
gemfile <<-G
source "https://notaserver.com"
@@ -434,7 +591,7 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
- it "when requiring fileutils after does not show redefinition warnings" do
+ it "when requiring fileutils after does not show redefinition warnings", :realworld do
dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false
skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty?
@@ -449,10 +606,11 @@ RSpec.describe "bundler/inline#gemfile" do
realworld_system_gems "pathname --version 0.2.0"
- realworld_system_gems "fiddle" # not sure why, but this is needed on Windows to boot rubygems successfully
-
realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems
+ # on prerelease rubies, a required_rubygems_version constraint is added by RubyGems to the resolution, causing Molinillo to load the `set` gem
+ realworld_system_gems "set --version 1.0.3" if Gem.ruby_version.prerelease?
+
script <<-RUBY, :dir => tmp("path_without_gemfile"), :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
require "bundler/inline"
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index a7161c9cfe..a9933f90e9 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
racc (1.5.2)
PLATFORMS
- #{lockfile_platforms_for(["ruby", specific_local_platform])}
+ #{lockfile_platforms("ruby")}
DEPENDENCIES
nokogiri (~> 1.11)
@@ -344,6 +344,23 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
end
+ it "pulls platform specific gems correctly on musl" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = "aarch64-linux"
+ end
+ end
+
+ simulate_platform "aarch64-linux-musl" do
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, :verbose => true
+ source "https://gems.repo4"
+ gem "nokogiri"
+ G
+ end
+
+ expect(out).to include("Fetching nokogiri 1.13.8 (aarch64-linux)")
+ end
+
it "allows specifying only-ruby-platform on windows with dependency platforms" do
simulate_windows do
install_gemfile <<-G
@@ -386,7 +403,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
s.add_dependency "platform_specific"
end
end
- simulate_windows x64_mingw do
+ simulate_windows x64_mingw32 do
lockfile <<-L
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -412,4 +429,36 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
end
end
+
+ %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch|
+ it "allows specifying platform windows on #{arch} arch" do
+ platform = send(arch.tr("-", "_"))
+
+ simulate_windows platform do
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ platform_specific (1.0-#{platform})
+ requires_platform_specific (1.0)
+ platform_specific
+
+ PLATFORMS
+ #{platform}
+
+ DEPENDENCIES
+ requires_platform_specific
+ L
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "platform_specific", :platforms => [:windows]
+ G
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}"
+ end
+ end
+ end
end
diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb
index 0032c6aef6..61cfc9b795 100644
--- a/spec/bundler/runtime/self_management_spec.rb
+++ b/spec/bundler/runtime/self_management_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
end
- it "shows a discreet message if locked bundler does not exist" do
+ it "shows a discrete message if locked bundler does not exist" do
missing_minor ="#{Bundler::VERSION[0]}.999.999"
lockfile_bundled_with(missing_minor)
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 29445b420c..2d39d72937 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -556,12 +556,12 @@ RSpec.describe "Bundler.setup" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "nonexistant"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "nonexistent"
G
bundle %(config set local.rack #{lib_path("local-rack")})
run "require 'rack'", :raise_on_error => false
- expect(err).to match(/is using branch main but Gemfile specifies nonexistant/)
+ expect(err).to match(/is using branch main but Gemfile specifies nonexistent/)
end
end
@@ -638,6 +638,16 @@ RSpec.describe "Bundler.setup" do
expect(err).to be_empty
end
+ it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bundler"
+ G
+
+ bundle "install --verbose", :env => { "BUNDLE_FROZEN" => "true" }
+ expect(err).to be_empty
+ end
+
it "doesn't re-resolve when deleting dependencies" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1304,18 +1314,14 @@ end
describe "default gem activation" do
let(:exemptions) do
- exempts = if Gem.rubygems_version >= Gem::Version.new("2.7")
- %w[did_you_mean]
- else
- %w[io-console openssl]
- end << "bundler"
- exempts << "fiddle" if Gem.win_platform? && Gem.rubygems_version >= Gem::Version.new("2.7")
+ exempts = %w[did_you_mean bundler]
exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7")
exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0")
exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6")
exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31")
exempts << "error_highlight" # added in Ruby 3.1 as a default gem
exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem
+ exempts << "syntax_suggest" # added in Ruby 3.2 as a default gem
exempts
end
@@ -1523,4 +1529,29 @@ end
expect(err).to be_empty
end
end
+
+ it "does not undo the Kernel.require decorations", :rubygems => ">= 3.4.6" do
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
+ script = bundled_app("bin/script")
+ create_file(script, <<~RUBY)
+ module Kernel
+ module_function
+
+ alias_method :require_before_extra_monkeypatches, :require
+
+ def require(path)
+ puts "requiring \#{path} used the monkeypatch"
+
+ require_before_extra_monkeypatches(path)
+ end
+ end
+
+ require "bundler/setup"
+
+ require "foo"
+ RUBY
+
+ sys_exec "#{Gem.ruby} #{script}", :raise_on_error => false
+ expect(out).to include("requiring foo used the monkeypatch")
+ end
end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 892ad10e98..6a7e2891a6 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -23,7 +23,6 @@ require_relative "support/indexes"
require_relative "support/matchers"
require_relative "support/permissions"
require_relative "support/platforms"
-require_relative "support/sudo"
$debug = false
@@ -40,7 +39,6 @@ RSpec.configure do |config|
config.include Spec::Matchers
config.include Spec::Path
config.include Spec::Platforms
- config.include Spec::Sudo
config.include Spec::Permissions
# Enable flags like --only-failures and --next-failure
@@ -60,6 +58,8 @@ RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
+
+ c.max_formatted_output_length = 1000
end
config.mock_with :rspec do |mocks|
@@ -96,27 +96,21 @@ RSpec.configure do |config|
end
config.around :each do |example|
- begin
- FileUtils.cp_r pristine_system_gem_path, system_gem_path
-
- with_gem_path_as(system_gem_path) do
- Bundler.ui.silence { example.run }
-
- all_output = all_commands_output
- if example.exception && !all_output.empty?
- message = all_output + "\n" + example.exception.message
- (class << example.exception; self; end).send(:define_method, :message) do
- message
- end
+ FileUtils.cp_r pristine_system_gem_path, system_gem_path
+
+ with_gem_path_as(system_gem_path) do
+ Bundler.ui.silence { example.run }
+
+ all_output = all_commands_output
+ if example.exception && !all_output.empty?
+ message = all_output + "\n" + example.exception.message
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
end
end
- ensure
- reset!
end
- end
-
- config.before :each, :sudo => true do
- Spec::Sudo.write_safe_config
+ ensure
+ reset!
end
config.after :suite do
diff --git a/spec/bundler/support/api_request_limit_hax.rb b/spec/bundler/support/api_request_limit_hax.rb
deleted file mode 100644
index 37ff0203b3..0000000000
--- a/spec/bundler/support/api_request_limit_hax.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
- require_relative "path"
- require "bundler/source"
- require "bundler/source/rubygems"
-
- module Bundler
- class Source
- class Rubygems < Source
- remove_const :API_REQUEST_LIMIT
- API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
- end
- end
- end
-end
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
index fb068fa9b5..ebc4d0ae5b 100644
--- a/spec/bundler/support/artifice/compact_index.rb
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -1,120 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
-require "compact_index"
-
-class CompactIndexAPI < Endpoint
- helpers do
- include Spec::Path
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
-
- def etag_response
- response_body = yield
- checksum = Digest(:MD5).hexdigest(response_body)
- return if not_modified?(checksum)
- headers "ETag" => quote(checksum)
- headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
- content_type "text/plain"
- requested_range_for(response_body)
- rescue StandardError => e
- puts e
- puts e.backtrace
- raise
- end
-
- def not_modified?(checksum)
- etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
-
- return unless etags.include?(checksum)
- headers "ETag" => quote(checksum)
- status 304
- body ""
- end
-
- def requested_range_for(response_body)
- ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
-
- if ranges
- status 206
- body ranges.map! {|range| slice_body(response_body, range) }.join
- else
- status 200
- body response_body
- end
- end
-
- def quote(string)
- %("#{string}")
- end
-
- def parse_etags(value)
- value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
- end
-
- def slice_body(body, range)
- body.byteslice(range)
- end
-
- def gems(gem_repo = default_gem_repo)
- @gems ||= {}
- @gems[gem_repo] ||= begin
- specs = Bundler::Deprecate.skip_during do
- %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
- load_spec(name, version, platform, gem_repo)
- end
- end.flatten
- end
-
- specs.group_by(&:name).map do |name, versions|
- gem_versions = versions.map do |spec|
- deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
- reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
- CompactIndex::Dependency.new(d.name, reqs)
- end
- checksum = begin
- Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
- rescue StandardError
- nil
- end
- CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
- deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
- end
- CompactIndex::Gem.new(name, gem_versions)
- end
- end
- end
- end
-
- get "/names" do
- etag_response do
- CompactIndex.names(gems.map(&:name))
- end
- end
-
- get "/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems)
- file.contents
- end
- end
-
- get "/info/:name" do
- etag_response do
- gem = gems.find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-end
+require_relative "helpers/compact_index"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexAPI)
diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb
index 6514fde01e..f771f7d1f0 100644
--- a/spec/bundler/support/artifice/compact_index_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_api_missing.rb
@@ -1,18 +1,13 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexApiMissing < CompactIndexAPI
get "/fetch/actual/gem/:id" do
- warn params[:id]
- if params[:id] == "rack-1.0.gemspec.rz"
- halt 404
- else
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
+ halt 404
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexApiMissing)
diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
index 775f1a3977..b9115cdd86 100644
--- a/spec/bundler/support/artifice/compact_index_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexBasicAuthentication < CompactIndexAPI
before do
@@ -12,4 +10,6 @@ class CompactIndexBasicAuthentication < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
index 1abe64236c..a6545b9ee4 100644
--- a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexChecksumMismatch < CompactIndexAPI
get "/versions" do
@@ -13,4 +11,6 @@ class CompactIndexChecksumMismatch < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexChecksumMismatch)
diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
index 14c31f35a4..35548f278c 100644
--- a/spec/bundler/support/artifice/compact_index_concurrent_download.rb
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexConcurrentDownload < CompactIndexAPI
get "/versions" do
@@ -29,4 +27,6 @@ class CompactIndexConcurrentDownload < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexConcurrentDownload)
diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
index cfe22c7f51..401e8a98d8 100644
--- a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexCredsDiffHost < CompactIndexAPI
helpers do
@@ -36,4 +34,6 @@ class CompactIndexCredsDiffHost < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexCredsDiffHost)
diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb
index cec368276a..cd41b3ecca 100644
--- a/spec/bundler/support/artifice/compact_index_extra.rb
+++ b/spec/bundler/support/artifice/compact_index_extra.rb
@@ -1,37 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtra < CompactIndexAPI
- get "/extra/versions" do
- halt 404
- end
-
- get "/extra/api/v1/dependencies" do
- halt 404
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo2}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo2}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtra)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb
index 5cc13421a8..8b9d304ab4 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api.rb
@@ -1,52 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtraApi < CompactIndexAPI
- get "/extra/names" do
- etag_response do
- CompactIndex.names(gems(gem_repo4).map(&:name))
- end
- end
-
- get "/extra/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems(gem_repo4))
- file.contents
- end
- end
-
- get "/extra/info/:name" do
- etag_response do
- gem = gems(gem_repo4).find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo4}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo4}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra_api"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtraApi)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
index b9d757c266..df6ede584c 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra_api"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra_api"
class CompactIndexExtraAPIMissing < CompactIndexExtraApi
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraAPIMissing < CompactIndexExtraApi
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraAPIMissing)
diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb
index ff1e47a1bb..255c89afdb 100644
--- a/spec/bundler/support/artifice/compact_index_extra_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra"
class CompactIndexExtraMissing < CompactIndexExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraMissing < CompactIndexExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraMissing)
diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb
index 3eebe0fbd8..18c30ed9a2 100644
--- a/spec/bundler/support/artifice/compact_index_forbidden.rb
+++ b/spec/bundler/support/artifice/compact_index_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexForbidden < CompactIndexAPI
get "/versions" do
@@ -10,4 +8,6 @@ class CompactIndexForbidden < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexForbidden)
diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb
index 304c897d68..9a711186db 100644
--- a/spec/bundler/support/artifice/compact_index_host_redirect.rb
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexHostRedirect < CompactIndexAPI
get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
@@ -18,4 +16,6 @@ class CompactIndexHostRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexHostRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_no_gem.rb b/spec/bundler/support/artifice/compact_index_no_gem.rb
index 0a4be08a46..71f6629688 100644
--- a/spec/bundler/support/artifice/compact_index_no_gem.rb
+++ b/spec/bundler/support/artifice/compact_index_no_gem.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexNoGem < CompactIndexAPI
get "/gems/:id" do
@@ -10,4 +8,6 @@ class CompactIndexNoGem < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexNoGem)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb
index cb1c7b9481..8c73011346 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexPartialUpdate < CompactIndexAPI
# Stub the server to never return 304s. This simulates the behaviour of
@@ -35,4 +33,6 @@ class CompactIndexPartialUpdate < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexPartialUpdate)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
index acf76dfbf0..20546ba4c3 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
def partial_update_no_etag
@@ -37,4 +35,6 @@ class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexPartialUpdateNoEtagNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_precompiled_before.rb b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
new file mode 100644
index 0000000000..b5f72f546a
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexPrecompiledBefore < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ move_ruby_variant_to_the_end(CompactIndex.info(gem ? gem.versions : []))
+ end
+ end
+
+ private
+
+ def move_ruby_variant_to_the_end(response)
+ lines = response.split("\n")
+ ruby = lines.find {|line| /\A\d+\.\d+\.\d* \|/.match(line) }
+ lines.delete(ruby)
+ lines.push(ruby).join("\n")
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPrecompiledBefore)
diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
index bb616125bb..8a7c4b79b0 100644
--- a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
+++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRangeNotSatisfiable < CompactIndexAPI
get "/versions" do
@@ -31,4 +29,6 @@ class CompactIndexRangeNotSatisfiable < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRangeNotSatisfiable)
diff --git a/spec/bundler/support/artifice/compact_index_rate_limited.rb b/spec/bundler/support/artifice/compact_index_rate_limited.rb
index 570105e2a0..4495491635 100644
--- a/spec/bundler/support/artifice/compact_index_rate_limited.rb
+++ b/spec/bundler/support/artifice/compact_index_rate_limited.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRateLimited < CompactIndexAPI
class RequestCounter
@@ -45,4 +43,6 @@ class CompactIndexRateLimited < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRateLimited)
diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb
index 99adc797bf..f7ba393239 100644
--- a/spec/bundler/support/artifice/compact_index_redirects.rb
+++ b/spec/bundler/support/artifice/compact_index_redirects.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRedirect < CompactIndexAPI
get "/fetch/actual/gem/:id" do
@@ -18,4 +16,6 @@ class CompactIndexRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
index 7d427b5382..96259385e7 100644
--- a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexStrictBasicAuthentication < CompactIndexAPI
before do
@@ -12,9 +10,11 @@ class CompactIndexStrictBasicAuthentication < CompactIndexAPI
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
index 036fac70b3..15850599b6 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongDependencies < CompactIndexAPI
get "/info/:name" do
@@ -14,4 +12,6 @@ class CompactIndexWrongDependencies < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongDependencies)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
index 8add32b88f..acc13a56ff 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongGemChecksum < CompactIndexAPI
get "/info/:name" do
@@ -17,4 +15,6 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongGemChecksum)
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
index c00113b28f..15242a7942 100644
--- a/spec/bundler/support/artifice/endpoint.rb
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -1,115 +1,6 @@
# frozen_string_literal: true
-require_relative "../path"
-
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-
-require "artifice"
-require "sinatra/base"
-
-ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
-ALL_REQUESTS_MUTEX = Thread::Mutex.new
-
-at_exit do
- if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
- expected = expected.split("\n").sort
- actual = ALL_REQUESTS.sort
-
- unless expected == actual
- raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
- end
- end
-end
-
-class Endpoint < Sinatra::Base
- def self.all_requests
- @all_requests ||= []
- end
-
- set :raise_errors, true
- set :show_exceptions, false
-
- def call!(*)
- super.tap do
- ALL_REQUESTS_MUTEX.synchronize do
- ALL_REQUESTS << @request.url
- end
- end
- end
-
- helpers do
- include Spec::Path
-
- def default_gem_repo
- if ENV["BUNDLER_SPEC_GEM_REPO"]
- Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"])
- else
- case request.host
- when "gem.repo1"
- Spec::Path.gem_repo1
- when "gem.repo2"
- Spec::Path.gem_repo2
- when "gem.repo3"
- Spec::Path.gem_repo3
- when "gem.repo4"
- Spec::Path.gem_repo4
- else
- Spec::Path.gem_repo1
- end
- end
- end
-
- def dependencies_for(gem_names, gem_repo = default_gem_repo)
- return [] if gem_names.nil? || gem_names.empty?
-
- all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read)
- end.inject(:+)
-
- all_specs.map do |name, version, platform|
- spec = load_spec(name, version, platform, gem_repo)
- next unless gem_names.include?(spec.name)
- {
- :name => spec.name,
- :number => spec.version.version,
- :platform => spec.platform.to_s,
- :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
- [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
- end,
- }
- end.compact
- end
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
- end
-
- get "/quick/Marshal.4.8/:id" do
- redirect "/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/fetch/actual/gem/:id" do
- File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/gems/:id" do
- File.binread("#{default_gem_repo}/gems/#{params[:id]}")
- end
-
- get "/api/v1/dependencies" do
- Marshal.dump(dependencies_for(params[:gems]))
- end
-
- get "/specs.4.8.gz" do
- File.binread("#{default_gem_repo}/specs.4.8.gz")
- end
-
- get "/prerelease_specs.4.8.gz" do
- File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
- end
-end
+require_relative "helpers/endpoint"
+require_relative "helpers/artifice"
Artifice.activate_with(Endpoint)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
index a0d850a44d..d8ab6b65bc 100644
--- a/spec/bundler/support/artifice/endpoint_500.rb
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -2,17 +2,16 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Endpoint500 < Sinatra::Base
before do
halt 500
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Endpoint500)
diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
index edc2463424..6bdc5896d6 100644
--- a/spec/bundler/support/artifice/endpoint_api_forbidden.rb
+++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointApiForbidden < Endpoint
get "/api/v1/dependencies" do
@@ -10,4 +8,6 @@ class EndpointApiForbidden < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointApiForbidden)
diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
index ff3d1493d6..e8e3569e63 100644
--- a/spec/bundler/support/artifice/endpoint_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointBasicAuthentication < Endpoint
before do
@@ -12,4 +10,6 @@ class EndpointBasicAuthentication < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
index 8b8972cedd..ce30de0a68 100644
--- a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointCredsDiffHost < Endpoint
helpers do
@@ -36,4 +34,6 @@ class EndpointCredsDiffHost < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointCredsDiffHost)
diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb
index 942c4352b7..021fd435fe 100644
--- a/spec/bundler/support/artifice/endpoint_extra.rb
+++ b/spec/bundler/support/artifice/endpoint_extra.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtra < Endpoint
get "/extra/api/v1/dependencies" do
@@ -30,4 +28,6 @@ class EndpointExtra < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtra)
diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb
index 1cfef7a7fc..a965af6e73 100644
--- a/spec/bundler/support/artifice/endpoint_extra_api.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_api.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtraApi < Endpoint
get "/extra/api/v1/dependencies" do
@@ -31,4 +29,6 @@ class EndpointExtraApi < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraApi)
diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb
index 5fd9238207..73e2defb32 100644
--- a/spec/bundler/support/artifice/endpoint_extra_missing.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_extra"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_extra"
class EndpointExtraMissing < EndpointExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointExtraMissing < EndpointExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraMissing)
diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb
index 08edf232e3..742e563f07 100644
--- a/spec/bundler/support/artifice/endpoint_fallback.rb
+++ b/spec/bundler/support/artifice/endpoint_fallback.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointFallback < Endpoint
DEPENDENCY_LIMIT = 60
@@ -16,4 +14,6 @@ class EndpointFallback < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointFallback)
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
index 338cbcad00..0efb6cda02 100644
--- a/spec/bundler/support/artifice/endpoint_host_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointHostRedirect < Endpoint
get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
@@ -14,4 +12,6 @@ class EndpointHostRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
index 22c13e3e17..74ce321de6 100644
--- a/spec/bundler/support/artifice/endpoint_marshal_fail.rb
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
@@ -1,13 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
-
-class EndpointMarshalFail < EndpointFallback
- get "/api/v1/dependencies" do
- "f0283y01hasf"
- end
-end
+require_relative "helpers/endpoint_marshal_fail"
+require_relative "helpers/artifice"
Artifice.activate_with(EndpointMarshalFail)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
index c341c3993f..ea4cfbe965 100644
--- a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_marshal_fail"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_marshal_fail"
class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
before do
@@ -12,4 +10,6 @@ class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
index 788a9027f3..6ea1a77eca 100644
--- a/spec/bundler/support/artifice/endpoint_mirror_source.rb
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
+require_relative "helpers/endpoint"
class EndpointMirrorSource < Endpoint
get "/gems/:id" do
@@ -12,4 +12,6 @@ class EndpointMirrorSource < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointMirrorSource)
diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb
index ee97fccf64..84f546ba9d 100644
--- a/spec/bundler/support/artifice/endpoint_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointRedirect < Endpoint
get "/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
index 4d4da08770..dff360c5c5 100644
--- a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointStrictBasicAuthentication < Endpoint
before do
@@ -12,9 +10,11 @@ class EndpointStrictBasicAuthentication < Endpoint
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb
index c118da1893..86b793e499 100644
--- a/spec/bundler/support/artifice/endpoint_timeout.rb
+++ b/spec/bundler/support/artifice/endpoint_timeout.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_fallback"
class EndpointTimeout < EndpointFallback
SLEEP_TIMEOUT = 3
@@ -12,4 +10,6 @@ class EndpointTimeout < EndpointFallback
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointTimeout)
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
index f69f2eccc6..6286e43fbd 100644
--- a/spec/bundler/support/artifice/fail.rb
+++ b/spec/bundler/support/artifice/fail.rb
@@ -2,10 +2,6 @@
require "net/http"
-# We can't use artifice here because it uses rack
-
-module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse
-
class Fail < Net::HTTP
# Net::HTTP uses a @newimpl instance variable to decide whether
# to use a legacy implementation. Since we are subclassing
@@ -27,8 +23,7 @@ class Fail < Net::HTTP
end
end
+require_relative "helpers/artifice"
+
# Replace Net::HTTP with our failing subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, ::Fail)
-end
+Artifice.replace_net_http(::Fail)
diff --git a/spec/bundler/support/artifice/helpers/artifice.rb b/spec/bundler/support/artifice/helpers/artifice.rb
new file mode 100644
index 0000000000..b8c78614fb
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/artifice.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+# This module was initially borrowed from https://github.com/wycats/artifice
+module Artifice
+ # Activate Artifice with a particular Rack endpoint.
+ #
+ # Calling this method will replace the Net::HTTP system
+ # with a replacement that routes all requests to the
+ # Rack endpoint.
+ #
+ # @param [#call] endpoint A valid Rack endpoint
+ def self.activate_with(endpoint)
+ require_relative "rack_request"
+
+ Net::HTTP.endpoint = endpoint
+ replace_net_http(Artifice::Net::HTTP)
+ end
+
+ # Deactivate the Artifice replacement.
+ def self.deactivate
+ replace_net_http(::Net::HTTP)
+ end
+
+ def self.replace_net_http(value)
+ ::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, value)
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
new file mode 100644
index 0000000000..4df47a9659
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
+require "compact_index"
+
+class CompactIndexAPI < Endpoint
+ helpers do
+ include Spec::Path
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+
+ def etag_response
+ response_body = yield
+ checksum = Digest(:MD5).hexdigest(response_body)
+ return if not_modified?(checksum)
+ headers "ETag" => quote(checksum)
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ rescue StandardError => e
+ puts e
+ puts e.backtrace
+ raise
+ end
+
+ def not_modified?(checksum)
+ etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
+
+ return unless etags.include?(checksum)
+ headers "ETag" => quote(checksum)
+ status 304
+ body ""
+ end
+
+ def requested_range_for(response_body)
+ ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
+
+ if ranges
+ status 206
+ body ranges.map! {|range| slice_body(response_body, range) }.join
+ else
+ status 200
+ body response_body
+ end
+ end
+
+ def quote(string)
+ %("#{string}")
+ end
+
+ def parse_etags(value)
+ value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
+ end
+
+ def slice_body(body, range)
+ body.byteslice(range)
+ end
+
+ def gems(gem_repo = default_gem_repo)
+ @gems ||= {}
+ @gems[gem_repo] ||= begin
+ specs = Bundler::Deprecate.skip_during do
+ %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
+ load_spec(name, version, platform, gem_repo)
+ end
+ end.flatten
+ end
+
+ specs.group_by(&:name).map do |name, versions|
+ gem_versions = versions.map do |spec|
+ deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
+ reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
+ CompactIndex::Dependency.new(d.name, reqs)
+ end
+ checksum = begin
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
+ rescue StandardError
+ nil
+ end
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ end
+ CompactIndex::Gem.new(name, gem_versions)
+ end
+ end
+ end
+ end
+
+ get "/names" do
+ etag_response do
+ CompactIndex.names(gems.map(&:name))
+ end
+ end
+
+ get "/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra.rb b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
new file mode 100644
index 0000000000..9e742630dd
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtra < CompactIndexAPI
+ get "/extra/versions" do
+ halt 404
+ end
+
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
new file mode 100644
index 0000000000..d9a7d83d23
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtraApi < CompactIndexAPI
+ get "/extra/names" do
+ etag_response do
+ CompactIndex.names(gems(gem_repo4).map(&:name))
+ end
+ end
+
+ get "/extra/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems(gem_repo4))
+ file.contents
+ end
+ end
+
+ get "/extra/info/:name" do
+ etag_response do
+ gem = gems(gem_repo4).find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb
new file mode 100644
index 0000000000..fc0381dc38
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require_relative "../../path"
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+
+require "sinatra/base"
+
+ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
+ALL_REQUESTS_MUTEX = Thread::Mutex.new
+
+at_exit do
+ if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
+ expected = expected.split("\n").sort
+ actual = ALL_REQUESTS.sort
+
+ unless expected == actual
+ raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
+ end
+ end
+end
+
+class Endpoint < Sinatra::Base
+ def self.all_requests
+ @all_requests ||= []
+ end
+
+ set :raise_errors, true
+ set :show_exceptions, false
+
+ def call!(*)
+ super.tap do
+ ALL_REQUESTS_MUTEX.synchronize do
+ ALL_REQUESTS << @request.url
+ end
+ end
+ end
+
+ helpers do
+ include Spec::Path
+
+ def default_gem_repo
+ if ENV["BUNDLER_SPEC_GEM_REPO"]
+ Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"])
+ else
+ case request.host
+ when "gem.repo1"
+ Spec::Path.gem_repo1
+ when "gem.repo2"
+ Spec::Path.gem_repo2
+ when "gem.repo3"
+ Spec::Path.gem_repo3
+ when "gem.repo4"
+ Spec::Path.gem_repo4
+ else
+ Spec::Path.gem_repo1
+ end
+ end
+ end
+
+ def dependencies_for(gem_names, gem_repo = default_gem_repo)
+ return [] if gem_names.nil? || gem_names.empty?
+
+ all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
+ Marshal.load(File.open(gem_repo.join(filename)).read)
+ end.inject(:+)
+
+ all_specs.map do |name, version, platform|
+ spec = load_spec(name, version, platform, gem_repo)
+ next unless gem_names.include?(spec.name)
+ {
+ :name => spec.name,
+ :number => spec.version.version,
+ :platform => spec.platform.to_s,
+ :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
+ [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
+ end,
+ }
+ end.compact
+ end
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+ end
+
+ get "/quick/Marshal.4.8/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/fetch/actual/gem/:id" do
+ File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/gems/:id" do
+ File.binread("#{default_gem_repo}/gems/#{params[:id]}")
+ end
+
+ get "/api/v1/dependencies" do
+ Marshal.dump(dependencies_for(params[:gems]))
+ end
+
+ get "/specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/specs.4.8.gz")
+ end
+
+ get "/prerelease_specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_extra.rb b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
new file mode 100644
index 0000000000..ad08495b50
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointExtra < Endpoint
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_fallback.rb b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
new file mode 100644
index 0000000000..a232930b67
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointFallback < Endpoint
+ DEPENDENCY_LIMIT = 60
+
+ get "/api/v1/dependencies" do
+ if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT
+ Marshal.dump(dependencies_for(params[:gems]))
+ else
+ halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
new file mode 100644
index 0000000000..c409d39d99
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "endpoint_fallback"
+
+class EndpointMarshalFail < EndpointFallback
+ get "/api/v1/dependencies" do
+ "f0283y01hasf"
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/rack_request.rb b/spec/bundler/support/artifice/helpers/rack_request.rb
new file mode 100644
index 0000000000..c4a07812a6
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/rack_request.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require "rack/test"
+require "net/http"
+
+module Artifice
+ module Net
+ # This is an internal object that can receive Rack requests
+ # to the application using the Rack::Test API
+ class RackRequest
+ include Rack::Test::Methods
+ attr_reader :app
+
+ def initialize(app)
+ @app = app
+ end
+ end
+
+ class HTTP < ::Net::HTTP
+ class << self
+ attr_accessor :endpoint
+ end
+
+ # Net::HTTP uses a @newimpl instance variable to decide whether
+ # to use a legacy implementation. Since we are subclassing
+ # Net::HTTP, we must set it
+ @newimpl = true
+
+ # We don't need to connect, so blank out this method
+ def connect
+ end
+
+ # Replace the Net::HTTP request method with a method
+ # that converts the request into a Rack request and
+ # dispatches it to the Rack endpoint.
+ #
+ # @param [Net::HTTPRequest] req A Net::HTTPRequest
+ # object, or one if its subclasses
+ # @param [optional, String, #read] body This should
+ # be sent as "rack.input". If it's a String, it will
+ # be converted to a StringIO.
+ # @return [Net::HTTPResponse]
+ #
+ # @yield [Net::HTTPResponse] If a block is provided,
+ # this method will yield the Net::HTTPResponse to
+ # it after the body is read.
+ def request(req, body = nil, &block)
+ rack_request = RackRequest.new(self.class.endpoint)
+
+ req.each_header do |header, value|
+ rack_request.header(header, value)
+ end
+
+ scheme = use_ssl? ? "https" : "http"
+ prefix = "#{scheme}://#{addr_port}"
+ body_stream_contents = req.body_stream.read if req.body_stream
+
+ response = rack_request.request("#{prefix}#{req.path}",
+ { :method => req.method, :input => body || req.body || body_stream_contents })
+
+ make_net_http_response(response, &block)
+ end
+
+ private
+
+ # This method takes a Rack response and creates a Net::HTTPResponse
+ # Instead of trying to mock HTTPResponse directly, we just convert
+ # the Rack response into a String that looks like a normal HTTP
+ # response and call Net::HTTPResponse.read_new
+ #
+ # @param [Array(#to_i, Hash, #each)] response a Rack response
+ # @return [Net::HTTPResponse]
+ # @yield [Net::HTTPResponse] If a block is provided, yield the
+ # response to it after the body is read
+ def make_net_http_response(response)
+ status = response.status
+ headers = response.headers
+ body = response.body
+
+ response_string = []
+ response_string << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value}"
+ end
+
+ response_string << "" << body
+
+ response_io = ::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
+ res = ::Net::HTTPResponse.read_new(response_io)
+
+ res.reading_body(response_io, true) do
+ yield res if block_given?
+ end
+
+ res
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/used_cassettes.txt b/spec/bundler/support/artifice/used_cassettes.txt
new file mode 100644
index 0000000000..a96efc790d
--- /dev/null
+++ b/spec/bundler/support/artifice/used_cassettes.txt
@@ -0,0 +1,20908 @@
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ffi-1.15.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/capybara/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/selenium-webdriver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/xpath/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/culerity/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uglifier/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/matrix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyzip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/childprocess/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libwebsocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-active_record/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/therubyracer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gherkin3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-wire/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/event-bus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-formatter-dots/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-html-formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-gherkin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-cucumber-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-messages/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-create-meta/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-uname/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/database_cleaner-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libv8/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trollop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/c21e/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag_expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cucumber-tag-expressions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-protobuf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/protobuf-cucumber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/curses/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis-namespace/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/resque/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rufus-scheduler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mono_logger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/et-orbi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/vegas/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fugit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/raabro/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.2.3.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-4.5.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mono_logger-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/ruby2_keywords-0.0.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tilt-2.0.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/mustermann-1.1.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/redis-namespace-1.6.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/vegas-0.1.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-protection-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/sinatra-2.1.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/tzinfo-2.0.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-1.24.1.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rufus-scheduler-2.0.24.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/resque-scheduler-2.2.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/climate_control/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cocaine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/paperclip/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/terrapin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_aws/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mocha/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thoughtbot-shoulda/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metaclass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/right_http_connection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gxapi_rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-api-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sass-listen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/addressable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/extlib/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/signet/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpadapter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/liquid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/launchy/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/autoparse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sinatra/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uuidtools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jwt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/retriable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/googleauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hurley/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoist/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/representable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-generator/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sassc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fchange/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-fsevent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-inotify/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rb-kqueue/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_dep/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-io/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/public_suffix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/english/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-protection/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongrel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mustermann/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multipart-post/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_http/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-net_http_persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-em_synchrony/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-httpclient/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-patron/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday-rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/logging/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/os/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hooks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/uber/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/declarative-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/trailblazer-option/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrick/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/google-apis-discovery_v1/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gems/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/cgi_multipart_eof_fix/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/fastthread/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gem_plugin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/escape_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/excon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-http-persistent/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/flexmock/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/little-plugger/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hashie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faraday_middleware/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rash/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/oauth2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/roauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/httpauth/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.10.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongoid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongodb-mongo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/durran-validatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mislav-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mongo_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/leshill-will_paginate/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bson/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/origin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/moped/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby2_keywords/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pry/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/optionable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/connection_pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coderay/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spoon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coveralls/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/metasploit-erd/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/yard/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-graphviz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/choice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/webrat/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/railties/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-mocks/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-core/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-expectations/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-collection_matchers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-support/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov-html/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/docile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/lockfile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/colorize/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/simplecov_json_formatter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rest-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tins/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/term-ansicolor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activeresource/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionwebservice/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionview/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activejob/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actioncable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activestorage/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actiontext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/actionmailbox/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-ssl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rexml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nokogiri/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hpricot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-test/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/abstract/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rdoc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/treetop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-mount/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack-cache/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sprockets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/journey/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-deprecated_sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-dom-testing/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-html-sanitizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activerecord-deprecated_finders/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/arel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/netrc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-cookie/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/http-accept/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sync/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-format/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rails-observers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-serializers-xml/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/erubi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/marcel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_mime/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mimemagic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activemodel-globalid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-rails/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/em-hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faye-websocket/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-driver/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/redis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nio4r/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facets/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/polyglot/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/weakling/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mini_portile2/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pkg-config/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/racc/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multimap/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/loofah/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hike/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tilt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mime-types-data/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/domain_name/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-essentials/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sqlite3/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tlsmail/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hiredis/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/text-hyphen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/eventmachine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timers/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/facter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-extras/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-fsm/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-pool/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/nenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/celluloid-supervision/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rspec-logsplit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/websocket-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ftp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/crass/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake-compiler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hitimes/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/CFPropertyList/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sys-admin/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32console/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-dir/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-api/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/windows-pr/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hocon/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/win32-security/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/dotenv-deployment/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coffee-script-source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/daemons/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/execjs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/time/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/configuration/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/mkrf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-protocol/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unf_ext/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rainbow/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/backports/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/powerpack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-progressbar/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/astrolabe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jaro_winkler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/parallel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubocop-ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/regexp_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/psych/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/test-unit/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/libxml-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi-win32-extensions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/date/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/io-wait/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/timeout/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ast/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/slop/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jar-dependencies/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strscan/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/power_assert/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pattern-match/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/maven-tools/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby-maven-libs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/virtus/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/axiom-types/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/coercible/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equalizer/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/descendants_tracker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/adamantium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ice_nine/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memoizable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bundler/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/bundler-2.3.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/concurrent-ruby-1.1.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.9.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.12.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/activesupport/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/i18n/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/faker/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/builder/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/memcache-client/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tzinfo/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thread_safe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/minitest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/method_source/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/zeitwerk/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/concurrent-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/multi_json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/pastel/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/thor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-pager/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-screen/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-tree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/RubyInline/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/hoe/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ZenTest/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/atomic/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/functional-ruby/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ruby_parser/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/equatable/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-which/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/tty-color/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ref/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/verse/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rubyforge/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/gemcutter/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/sexp_processor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ParseTree/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode_utils/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/unicode-display_width/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/strings-ansi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rake/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-scp/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json_pure/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/SexpProcessor/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/spruz/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/net-ssh/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/jruby-pageant/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rbnacl-libsodium/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/bcrypt_pbkdf/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/needle/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/ffi/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/i18n-0.6.11.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/multi_json-1.15.0.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/activesupport-3.2.22.5.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/faker-1.1.2.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/diff-lcs/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/diff-lcs-1.4.4.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/rack/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/info/json/GET/response
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/request
+spec/support/artifice/vcr_cassettes/realworld/rubygems.org/gems/rack-2.0.9.gem/GET/response
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/request
+spec/support/artifice/vcr_cassettes/realworld/index.rubygems.org/versions/GET/response
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index ceb133346f..6a346f1ff9 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -4,6 +4,7 @@ require "net/http"
require_relative "../path"
CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes"
+USED_CASSETTES_PATH = "#{Spec::Path.spec_dir}/support/artifice/used_cassettes.txt"
CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
class BundlerVCRHTTP < Net::HTTP
@@ -22,6 +23,10 @@ class BundlerVCRHTTP < Net::HTTP
@__vcr_request_handler = handler
end
+ File.open(USED_CASSETTES_PATH, "a+") do |f|
+ f.puts request_pair_paths.map {|path| Pathname.new(path).relative_path_from(Spec::Path.source_root).to_s }.join("\n")
+ end
+
if recorded_response?
recorded_response
else
@@ -42,7 +47,7 @@ class BundlerVCRHTTP < Net::HTTP
response.uri = request.uri
response.reading_body(response_io, request.response_body_permitted?) do
- response_block.call(response) if response_block
+ response_block&.call(response)
end
end
end
@@ -74,25 +79,8 @@ class BundlerVCRHTTP < Net::HTTP
def request_pair_paths
%w[request response].map do |kind|
- File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key + [kind]))
- end
- end
-
- def read_stored_request(path)
- contents = File.binread(path)
- headers = {}
- method = nil
- path = nil
- contents.lines.grep(/^> /).each do |line|
- if line =~ /^> (GET|HEAD|POST|PATCH|PUT|DELETE) (.*)/
- method = $1
- path = $2.strip
- elsif line =~ /^> (.*?): (.*)/
- headers[$1] = $2
- end
+ File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key), kind)
end
- body = contents =~ /^([^>].*)/m && $1
- Net::HTTP.const_get(method.capitalize).new(path, headers).tap {|r| r.body = body if body }
end
def request_to_string(request)
@@ -158,8 +146,7 @@ class BundlerVCRHTTP < Net::HTTP
alias_method :request, :request_with_vcr
end
+require_relative "helpers/artifice"
+
# Replace Net::HTTP with our VCR subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, BundlerVCRHTTP)
-end
+Artifice.replace_net_http(BundlerVCRHTTP)
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
index 674470688d..4d90e0a426 100644
--- a/spec/bundler/support/artifice/windows.rb
+++ b/spec/bundler/support/artifice/windows.rb
@@ -2,13 +2,10 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Windows < Sinatra::Base
set :raise_errors, true
set :show_exceptions, false
@@ -43,4 +40,6 @@ class Windows < Sinatra::Base
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Windows)
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index a4d4c9f085..dfc7139523 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -17,18 +17,6 @@ module Spec
Gem::Platform.new(platform)
end
- # Returns a number smaller than the size of the index. Useful for specs that
- # need the API request limit to be reached for some reason.
- def low_api_request_limit_for(gem_repo)
- all_gems = Dir[gem_repo.join("gems/*.gem")]
-
- all_gem_names = all_gems.map do |file|
- File.basename(file, ".gem").match(/\A(?<gem_name>[^-]+)-.*\z/)[:gem_name]
- end.uniq
-
- (all_gem_names - ["bundler"]).size
- end
-
def build_repo1
rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
@@ -110,19 +98,27 @@ module Spec
build_gem "platform_specific" do |s|
s.platform = "x86-mswin32"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 MSWIN'"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mswin32'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x64-mswin64"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mswin64'"
end
build_gem "platform_specific" do |s|
s.platform = "x86-mingw32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mingw32'"
end
build_gem "platform_specific" do |s|
s.platform = "x64-mingw32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw32'"
end
build_gem "platform_specific" do |s|
s.platform = "x64-mingw-ucrt"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw-ucrt'"
end
build_gem "platform_specific" do |s|
@@ -212,10 +208,8 @@ module Spec
update_repo(gem_repo4, &blk)
end
- def update_repo2
- update_repo gem_repo2 do
- yield if block_given?
- end
+ def update_repo2(&blk)
+ update_repo(gem_repo2, &blk)
end
def build_security_repo
@@ -290,10 +284,6 @@ module Spec
end
end
- def build_dep(name, requirements = Gem::Requirement.default, type = :runtime)
- Bundler::Dependency.new(name, :version => requirements)
- end
-
def build_lib(name, *args, &blk)
build_with(LibBuilder, name, args, &blk)
end
@@ -448,7 +438,7 @@ module Spec
write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"] unless RUBY_VERSION < "2.4"
+ $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
extension_name = "#{name}_c"
if extra_lib_dir = with_config("ext-lib")
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
index bb21526d35..5f808531ff 100644
--- a/spec/bundler/support/bundle.rb
+++ b/spec/bundler/support/bundle.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
require "rubygems"
+Gem.instance_variable_set(:@ruby, ENV["RUBY"]) if ENV["RUBY"]
+
require_relative "path"
bundler_gemspec = Spec::Path.loaded_gemspec
bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root)
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index c3b7a425ae..78545d2e64 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require_relative "sudo"
-
class RequirementChecker < Proc
def self.against(present)
provided = Gem::Version.new(present)
@@ -21,10 +19,9 @@ class RequirementChecker < Proc
end
RSpec.configure do |config|
- config.filter_run_excluding :sudo => true
config.filter_run_excluding :realworld => true
- git_version = Bundler::Source::Git::GitProxy.new(nil, nil, nil).version
+ git_version = Bundler::Source::Git::GitProxy.new(nil, nil).version
config.filter_run_excluding :git => RequirementChecker.against(git_version)
config.filter_run_excluding :bundler => RequirementChecker.against(Bundler::VERSION.split(".")[0])
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index da67e8c5d1..c7fe3637cc 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -1,5 +1,10 @@
# frozen_string_literal: true
+if ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ Object.send(:remove_const, :RUBY_PLATFORM)
+ RUBY_PLATFORM = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+end
+
module Gem
def self.ruby=(ruby)
@ruby = ruby
@@ -19,20 +24,30 @@ module Gem
end
if ENV["BUNDLER_SPEC_PLATFORM"]
+ previous_platforms = @platforms
+ previous_local = Platform.local
+
class Platform
@local = new(ENV["BUNDLER_SPEC_PLATFORM"])
end
- @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
+ @platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform }
end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
- # We only need this hack for rubygems versions without the BundlerVersionFinder
- if Gem.rubygems_version < Gem::Version.new("2.7.0")
- @path_to_default_spec_map.delete_if do |_path, spec|
- spec.name == "bundler"
+ if ENV["BUNDLER_IGNORE_DEFAULT_GEM"]
+ module RemoveDefaultBundlerStub
+ def default_stubs(pattern = "*")
+ super.delete_if {|stub| stub.name == "bundler" }
+ end
+ end
+
+ class Specification
+ class << self
+ prepend RemoveDefaultBundlerStub
+ end
end
end
end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index af6e338853..7b8c56b6ad 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -78,9 +78,6 @@ module Spec
end
def bundle(cmd, options = {}, &block)
- with_sudo = options.delete(:sudo)
- sudo = with_sudo == :preserve_env ? "sudo -E --preserve-env=RUBYOPT" : "sudo" if with_sudo
-
bundle_bin = options.delete(:bundle_bin)
bundle_bin ||= installed_bindir.join("bundle")
@@ -119,7 +116,7 @@ module Spec
end
end.join
- ruby_cmd = build_ruby_cmd({ :sudo => sudo, :load_path => load_path, :requires => requires })
+ ruby_cmd = build_ruby_cmd({ :load_path => load_path, :requires => requires })
cmd = "#{ruby_cmd} #{bundle_bin} #{cmd}#{args}"
sys_exec(cmd, { :env => env, :dir => dir, :raise_on_error => raise_on_error }, &block)
end
@@ -146,8 +143,6 @@ module Spec
end
def build_ruby_cmd(options = {})
- sudo = options.delete(:sudo)
-
libs = options.delete(:load_path)
lib_option = libs ? "-I#{libs.join(File::PATH_SEPARATOR)}" : []
@@ -155,7 +150,7 @@ module Spec
requires << "#{Path.spec_dir}/support/hax.rb"
require_option = requires.map {|r| "-r#{r}" }
- [sudo, Gem.ruby, *lib_option, *require_option].compact.join(" ")
+ [Gem.ruby, *lib_option, *require_option].compact.join(" ")
end
def gembin(cmd, options = {})
@@ -295,7 +290,7 @@ module Spec
if gem_name.start_with?("bundler")
version = gem_name.match(/\Abundler-(?<version>.*)\z/)[:version] if gem_name != "bundler"
with_built_bundler(version) {|gem_path| install_gem(gem_path, default) }
- elsif gem_name =~ %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}
+ elsif %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}.match?(gem_name)
install_gem(gem_name, default)
else
install_gem("#{gem_repo}/gems/#{gem_name}.gem", default)
@@ -307,7 +302,7 @@ module Spec
def install_gem(path, default = false)
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
- args = "--no-document --ignore-dependencies"
+ args = "--no-document --ignore-dependencies --verbose --local"
args += " --default --install-dir #{system_gem_path}" if default
gem_command "install #{args} '#{path}'"
@@ -419,14 +414,14 @@ module Spec
end
end
- def cache_gems(*gems)
+ def cache_gems(*gems, gem_repo: gem_repo1)
gems = gems.flatten
FileUtils.rm_rf("#{bundled_app}/vendor/cache")
FileUtils.mkdir_p("#{bundled_app}/vendor/cache")
gems.each do |g|
- path = "#{gem_repo1}/gems/#{g}.gem"
+ path = "#{gem_repo}/gems/#{g}.gem"
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
FileUtils.cp(path, "#{bundled_app}/vendor/cache")
end
@@ -437,6 +432,14 @@ module Spec
pristine_system_gems :bundler
end
+ def simulate_ruby_platform(ruby_platform)
+ old = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = ruby_platform.to_s
+ yield
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = old
+ end
+
def simulate_platform(platform)
old = ENV["BUNDLER_SPEC_PLATFORM"]
ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
@@ -445,7 +448,7 @@ module Spec
ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
end
- def simulate_windows(platform = mswin)
+ def simulate_windows(platform = x86_mswin32)
old = ENV["BUNDLER_SPEC_WINDOWS"]
ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do
@@ -480,13 +483,23 @@ module Spec
end
def next_ruby_minor
- Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
+ ruby_major_minor.map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
+ end
+
+ def previous_ruby_minor
+ return "2.7" if ruby_major_minor == [3, 0]
+
+ ruby_major_minor.map.with_index {|s, i| i == 1 ? s - 1 : s }.join(".")
+ end
+
+ def ruby_major_minor
+ Gem.ruby_version.segments[0..1]
end
- # versions providing a bundler version finder but not including
+ # versions not including
# https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
def rubygems_version_failing_to_activate_bundler_prereleases
- Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1") && Gem.rubygems_version >= Gem::Version.new("2.7.0")
+ Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1")
end
def revision_for(path)
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 55d798a90a..78372302f1 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -16,21 +16,23 @@ module Spec
def resolve(args = [])
@platforms ||= ["ruby"]
- deps = []
default_source = instance_double("Bundler::Source::Rubygems", :specs => @index, :to_s => "locally install gems")
source_requirements = { :default => default_source }
+ base = args[0] || Bundler::SpecSet.new([])
+ base.each {|ls| ls.source = default_source }
+ gem_version_promoter = args[1] || Bundler::GemVersionPromoter.new
+ originally_locked = args[2] || Bundler::SpecSet.new([])
+ unlock = args[3] || []
@deps.each do |d|
- source_requirements[d.name] = d.source = default_source
- @platforms.each do |p|
- deps << Bundler::DepProxy.get_proxy(d, p)
- end
+ name = d.name
+ source_requirements[name] = d.source = default_source
end
- args[0] ||= [] # base
- args[0].each {|ls| ls.source = default_source }
- args[1] ||= Bundler::GemVersionPromoter.new # gem_version_promoter
- args[2] ||= [] # additional_base_requirements
- args[3] ||= @platforms # platforms
- Bundler::Resolver.resolve(deps, source_requirements, *args)
+ packages = Bundler::Resolver::Base.new(source_requirements, @deps, base, @platforms, :locked_specs => originally_locked, :unlock => unlock)
+ Bundler::Resolver.new(packages, gem_version_promoter).start
+ end
+
+ def should_not_resolve
+ expect { resolve }.to raise_error(Bundler::GemNotFound)
end
def should_resolve_as(specs)
@@ -47,13 +49,6 @@ module Spec
end
end
- def should_conflict_on(names)
- got = resolve
- raise "The resolve succeeded with: #{got.map(&:full_name).sort.inspect}"
- rescue Bundler::VersionConflict => e
- expect(Array(names).sort).to eq(e.conflicts.sort)
- end
-
def gem(*args, &blk)
build_spec(*args, &blk).first
end
@@ -67,12 +62,11 @@ module Spec
def should_conservative_resolve_and_include(opts, unlock, specs)
# empty unlock means unlock all
opts = Array(opts)
- search = Bundler::GemVersionPromoter.new(@locked, unlock).tap do |s|
+ search = Bundler::GemVersionPromoter.new.tap do |s|
s.level = opts.first
s.strict = opts.include?(:strict)
- s.prerelease_specified = Hash[@deps.map {|d| [d.name, d.requirement.prerelease?] }]
end
- should_resolve_and_include specs, [@base, search]
+ should_resolve_and_include specs, [@base, search, @locked, unlock]
end
def an_awesome_index
@@ -127,7 +121,7 @@ module Spec
next if version == v("1.4.2.1") && platform != pl("x86-mswin32")
next if version == v("1.4.2") && platform == pl("x86-mswin32")
gem "nokogiri", version, platform do
- dep "weakling", ">= 0.0.3" if platform =~ pl("java")
+ dep "weakling", ">= 0.0.3" if platform =~ pl("java") # rubocop:disable Performance/RegexpMatch
end
end
end
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index ce6b216619..ea7c784683 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -97,6 +97,18 @@ module Spec
end
end
+ RSpec::Matchers.define :take_less_than do |seconds|
+ match do |actual|
+ start_time = Time.now
+
+ actual.call
+
+ (Time.now - start_time).to_f < seconds
+ end
+
+ supports_block_expectations
+ end
+
define_compound_matcher :read_as, [exist] do |file_contents|
diffable
@@ -150,7 +162,7 @@ module Spec
end
if exitstatus == 65
actual_platform = out.split("\n").last
- next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
+ next "#{name} was expected to be of platform #{platform || "ruby"} but was #{actual_platform || "ruby"}"
end
if exitstatus == 66
actual_source = out.split("\n").last
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index a39e46c78a..8b9c0e1290 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -71,10 +71,6 @@ module Spec
@spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
end
- def api_request_limit_hack_file
- spec_dir.join("support/api_request_limit_hax.rb")
- end
-
def man_dir
@man_dir ||= lib_dir.join("bundler/man")
end
@@ -118,6 +114,14 @@ module Spec
end
end
+ def default_cache_path(*path)
+ if Bundler.feature_flag.global_gem_cache?
+ home(".bundle/cache", *path)
+ else
+ default_bundle_path("cache/bundler", *path)
+ end
+ end
+
def bundled_app(*path)
root = tmp.join("bundled_app")
FileUtils.mkdir_p(root)
@@ -287,29 +291,19 @@ module Spec
end
def rubocop_gemfile_basename
- filename = if RUBY_VERSION.start_with?("2.3")
- "rubocop23_gems"
- elsif RUBY_VERSION.start_with?("2.4")
- "rubocop24_gems"
- else
- "rubocop_gems"
- end
- tool_dir.join("#{filename}.rb")
+ tool_dir.join("rubocop_gems.rb")
end
def standard_gemfile_basename
- filename = if RUBY_VERSION.start_with?("2.3")
- "standard23_gems"
- elsif RUBY_VERSION.start_with?("2.4")
- "standard24_gems"
- else
- "standard_gems"
- end
- tool_dir.join("#{filename}.rb")
+ tool_dir.join("standard_gems.rb")
end
def tool_dir
- source_root.join("tool/bundler")
+ ruby_core? ? source_root.join("tool/bundler") : source_root.join("../tool/bundler")
+ end
+
+ def templates_dir
+ lib_dir.join("bundler", "templates")
end
extend self
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index 1ad7778403..eca1b2e60d 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -21,31 +21,35 @@ module Spec
end
def linux
- Gem::Platform.new(["x86", "linux", nil])
+ Gem::Platform.new("x86_64-linux")
end
- def mswin
+ def x86_mswin32
Gem::Platform.new(["x86", "mswin32", nil])
end
- def mingw
+ def x64_mswin64
+ Gem::Platform.new(["x64", "mswin64", nil])
+ end
+
+ def x86_mingw32
Gem::Platform.new(["x86", "mingw32", nil])
end
- def x64_mingw
+ def x64_mingw32
Gem::Platform.new(["x64", "mingw32", nil])
end
- def all_platforms
- [rb, java, linux, mswin, mingw, x64_mingw]
+ def x64_mingw_ucrt
+ Gem::Platform.new(["x64", "mingw", "ucrt"])
end
- def local
- generic_local_platform
+ def windows_platforms
+ [x86_mswin32, x64_mswin64, x86_mingw32, x64_mingw32, x64_mingw_ucrt]
end
- def specific_local_platform
- Bundler.local_platform
+ def all_platforms
+ [rb, java, linux, windows_platforms].flatten
end
def not_local
@@ -56,14 +60,14 @@ module Spec
if RUBY_PLATFORM == "java"
:jruby
elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM)
- :x64_mingw
+ :windows
else
:ruby
end
end
def not_local_tag
- [:jruby, :x64_mingw, :ruby].find {|tag| tag != local_tag }
+ [:jruby, :windows, :ruby].find {|tag| tag != local_tag }
end
def local_ruby_engine
@@ -76,7 +80,7 @@ module Spec
def not_local_engine_version
case not_local_tag
- when :ruby, :x64_mingw
+ when :ruby, :windows
not_local_ruby_version
when :jruby
"1.6.1"
@@ -91,11 +95,11 @@ module Spec
9999
end
- def lockfile_platforms
- lockfile_platforms_for([specific_local_platform])
+ def lockfile_platforms(*extra)
+ formatted_lockfile_platforms(local_platform, *extra)
end
- def lockfile_platforms_for(platforms)
+ def formatted_lockfile_platforms(*platforms)
platforms.map(&:to_s).sort.join("\n ")
end
end
diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb
index d1b1f8dd03..5653601ae8 100644
--- a/spec/bundler/support/rubygems_version_manager.rb
+++ b/spec/bundler/support/rubygems_version_manager.rb
@@ -113,7 +113,7 @@ class RubygemsVersionManager
end
def resolve_target_tag
- return "v#{@source}" if @source.match(/^\d/)
+ return "v#{@source}" if @source.match?(/^\d/)
@source
end
diff --git a/spec/bundler/support/sudo.rb b/spec/bundler/support/sudo.rb
deleted file mode 100644
index 7b9b392754..0000000000
--- a/spec/bundler/support/sudo.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Spec
- module Sudo
- def self.present?
- @which_sudo ||= Bundler.which("sudo")
- end
-
- def self.write_safe_config
- File.write(Spec::Path.tmp("gitconfig"), "[safe]\n\tdirectory = #{Spec::Path.git_root}")
- end
-
- def sudo(cmd)
- raise "sudo not present" unless Sudo.present?
- sys_exec("sudo #{cmd}")
- end
-
- def chown_system_gems_to_root
- sudo "chown -R root #{system_gem_path}"
- end
- end
-end
diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb
index 0dfe63d36d..d80f4018f3 100644
--- a/spec/bundler/update/gems/fund_spec.rb
+++ b/spec/bundler/update/gems/fund_spec.rb
@@ -5,20 +5,20 @@ RSpec.describe "bundle update" do
build_repo2 do
build_gem "has_funding_and_other_metadata" do |s|
s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
"documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
}
end
build_gem "has_funding", "1.2.3" do |s|
s.metadata = {
- "funding_uri" => "https://example.com/has_funding/funding",
+ "funding_uri" => "https://example.com/has_funding/funding",
}
end
end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index da92cab1cc..59e3d2f5fb 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -120,10 +120,14 @@ RSpec.describe "bundle update" do
G
bundle "update", :all => true
+ expect(err).to be_empty
end
describe "with submodules" do
before :each do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_repo4 do
build_gem "submodule" do |s|
s.write "lib/submodule.rb", "puts 'GEM'"
diff --git a/spec/mspec/lib/mspec/commands/mkspec.rb b/spec/mspec/lib/mspec/commands/mkspec.rb
index d10cc35d18..a31cb2191c 100755
--- a/spec/mspec/lib/mspec/commands/mkspec.rb
+++ b/spec/mspec/lib/mspec/commands/mkspec.rb
@@ -95,7 +95,9 @@ class MkSpec
def write_spec(file, meth, exists)
if exists
- out = `#{ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}`
+ command = "#{RbConfig.ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}"
+ puts "$ #{command}" if $DEBUG
+ out = `#{command}`
return if out.include?(meth)
end
@@ -133,18 +135,6 @@ EOS
end
end
- ##
- # Determine and return the path of the ruby executable.
-
- def ruby
- ruby = File.join(RbConfig::CONFIG['bindir'],
- RbConfig::CONFIG['ruby_install_name'])
-
- ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR
-
- return ruby
- end
-
def self.main
ENV['MSPEC_RUNNER'] = '1'
diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
index 596b120d9f..69181b71d3 100644
--- a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
+++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
@@ -173,7 +173,8 @@ class LeakChecker
def find_threads
Thread.list.find_all {|t|
- t != Thread.current && t.alive?
+ t != Thread.current && t.alive? &&
+ !(t.thread_variable?(:"\0__detached_thread__") && t.thread_variable_get(:"\0__detached_thread__"))
}
end
diff --git a/spec/mspec/lib/mspec/utils/name_map.rb b/spec/mspec/lib/mspec/utils/name_map.rb
index a389b9d1de..bf70e651a2 100644
--- a/spec/mspec/lib/mspec/utils/name_map.rb
+++ b/spec/mspec/lib/mspec/utils/name_map.rb
@@ -51,6 +51,10 @@ class NameMap
SpecVersion
]
+ ALWAYS_PRIVATE = %w[
+ initialize initialize_copy initialize_clone initialize_dup respond_to_missing?
+ ].map(&:to_sym)
+
def initialize(filter = false)
@seen = {}
@filter = filter
@@ -86,7 +90,8 @@ class NameMap
hash["#{name}."] = ms.sort unless ms.empty?
ms = m.public_instance_methods(false) +
- m.protected_instance_methods(false)
+ m.protected_instance_methods(false) +
+ (m.private_instance_methods(false) & ALWAYS_PRIVATE)
ms.map! { |x| x.to_s }
hash["#{name}#"] = ms.sort unless ms.empty?
diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb
index b9f8b17fdc..e86beaab86 100644
--- a/spec/mspec/lib/mspec/utils/script.rb
+++ b/spec/mspec/lib/mspec/utils/script.rb
@@ -283,7 +283,6 @@ class MSpecScript
script = new
script.load_default
- script.try_load '~/.mspecrc'
script.options
script.signals
script.register
diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb
index 825add7212..32262723de 100644
--- a/spec/mspec/spec/commands/mkspec_spec.rb
+++ b/spec/mspec/spec/commands/mkspec_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe MkSpec, "#write_spec" do
end
it "checks if specs exist for the method if the spec file exists" do
- name = Regexp.escape(@script.ruby)
+ name = Regexp.escape(RbConfig.ruby)
expect(@script).to receive(:`).with(
%r"#{name} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e 'Object#inspect' spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb
index d9f6eac9a9..c35bda8b47 100644
--- a/spec/mspec/spec/utils/script_spec.rb
+++ b/spec/mspec/spec/utils/script_spec.rb
@@ -96,11 +96,6 @@ RSpec.describe MSpecScript, ".main" do
MSpecScript.main
end
- it "attempts to load the '~/.mspecrc' script" do
- expect(@script).to receive(:try_load).with('~/.mspecrc')
- MSpecScript.main
- end
-
it "calls the #options method on the script" do
expect(@script).to receive(:options)
MSpecScript.main
diff --git a/spec/mspec/tool/tag_from_output.rb b/spec/mspec/tool/tag_from_output.rb
index ebe13434c2..a6e60945cd 100755
--- a/spec/mspec/tool/tag_from_output.rb
+++ b/spec/mspec/tool/tag_from_output.rb
@@ -11,6 +11,11 @@ abort 'Could not find tags directory' unless tags_dir
output = ARGF.readlines
+# Automatically strip datetime of GitHub Actions
+if output.first =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z /
+ output = output.map { |line| line.split(' ', 2).last }
+end
+
NUMBER = /^\d+\)$/
ERROR_OR_FAILED = / (ERROR|FAILED)$/
SPEC_FILE = /^(\/.+_spec\.rb)\:\d+/
@@ -22,11 +27,24 @@ output.slice_before(NUMBER).select { |number, *rest|
description = error_line.match(ERROR_OR_FAILED).pre_match
spec_file = rest.find { |line| line =~ SPEC_FILE }
- unless spec_file
- warn "Could not find file for:\n#{error_line}"
- next
+ if spec_file
+ spec_file = spec_file[SPEC_FILE, 1] or raise
+ else
+ if error_line =~ /^(\w+)[#\.](\w+) /
+ module_method = error_line.split(' ', 2).first
+ file = "#{$1.downcase}/#{$2}_spec.rb"
+ spec_file = ['spec/ruby/core', 'spec/ruby/library', *Dir.glob('spec/ruby/library/*')].find { |dir|
+ path = "#{dir}/#{file}"
+ break path if File.exist?(path)
+ }
+ end
+
+ unless spec_file
+ warn "Could not find file for:\n#{error_line}"
+ next
+ end
end
- spec_file = spec_file[SPEC_FILE, 1]
+
prefix = spec_file.index('spec/ruby/') || spec_file.index('spec/truffle/')
spec_file = spec_file[prefix..-1]
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml
index 1200e9d7ce..82733c4b4d 100644
--- a/spec/ruby/.rubocop.yml
+++ b/spec/ruby/.rubocop.yml
@@ -115,6 +115,10 @@ Lint/EmptyWhen:
- language/case_spec.rb
- optional/capi/spec_helper.rb
+Lint/ErbNewArguments:
+ Exclude:
+ - 'library/erb/new_spec.rb'
+
Lint/FormatParameterMismatch:
Exclude:
- 'core/kernel/shared/sprintf.rb'
@@ -161,6 +165,9 @@ Lint/Debugger:
Lint/Loop:
Enabled: false
+Style/BlockComments:
+ Enabled: true
+
Style/Lambda:
Enabled: true
EnforcedStyle: literal
diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md
index 20258e5c36..adfc2fb0ca 100644
--- a/spec/ruby/CONTRIBUTING.md
+++ b/spec/ruby/CONTRIBUTING.md
@@ -175,9 +175,10 @@ end
#### Guard for bug
-In case there is a bug in MRI but the expected behavior is obvious.
+In case there is a bug in MRI and the fix will be backported to previous versions.
+If it is not backported or not likely, use `ruby_version_is` instead.
First, file a bug at https://bugs.ruby-lang.org/.
-It is better to use a `ruby_version_is` guard if there was a release with the fix.
+The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
```ruby
ruby_bug '#13669', ''...'3.2' do
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index 55b248a6c3..018bf0ca3e 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -1,7 +1,6 @@
# The Ruby Spec Suite
[![Actions Build Status](https://github.com/ruby/spec/workflows/CI/badge.svg)](https://github.com/ruby/spec/actions)
-[![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec)
The Ruby Spec Suite, abbreviated `ruby/spec`, is a test suite for the behavior of the Ruby programming language.
@@ -144,10 +143,9 @@ The file `/etc/services` is required for socket specs (package `netbase` on Debi
### Socket specs from rubysl-socket
-Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
+Most specs under `library/socket` were imported from the rubysl-socket project (which is no longer on GitHub).
The 3 copyright holders of rubysl-socket, Yorick Peterse, Chuck Remes and
-Brian Shirai, [agreed to relicense those specs](https://github.com/rubysl/rubysl-socket/issues/15)
-under the MIT license in ruby/spec.
+Brian Shirai, agreed to relicense those specs under the MIT license in ruby/spec.
### History and RubySpec
diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb
index bf2bdeaf91..40f7329b7c 100644
--- a/spec/ruby/core/array/keep_if_spec.rb
+++ b/spec/ruby/core/array/keep_if_spec.rb
@@ -1,3 +1,4 @@
+require_relative '../../spec_helper'
require_relative 'shared/keep_if'
describe "Array#keep_if" do
diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb
index 7200830331..be03551629 100644
--- a/spec/ruby/core/array/pack/c_spec.rb
+++ b/spec/ruby/core/array/pack/c_spec.rb
@@ -45,8 +45,18 @@ describe :array_pack_8bit, shared: true do
[1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/m_spec.rb b/spec/ruby/core/array/pack/m_spec.rb
index 2b1a84abca..c6364af12d 100644
--- a/spec/ruby/core/array/pack/m_spec.rb
+++ b/spec/ruby/core/array/pack/m_spec.rb
@@ -80,8 +80,16 @@ describe "Array#pack with format 'M'" do
].should be_computed_by(:pack, "M")
end
- it "encodes a tab followed by a newline with an encoded newline" do
+ it "encodes a tab at the end of a line with an encoded newline" do
+ ["\t"].pack("M").should == "\t=\n"
["\t\n"].pack("M").should == "\t=\n\n"
+ ["abc\t\nxyz"].pack("M").should == "abc\t=\n\nxyz=\n"
+ end
+
+ it "encodes a space at the end of a line with an encoded newline" do
+ [" "].pack("M").should == " =\n"
+ [" \n"].pack("M").should == " =\n\n"
+ ["abc \nxyz"].pack("M").should == "abc =\n\nxyz=\n"
end
it "encodes 127..255 in hex format" do
diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb
index 23e239d3de..65fdaa45d8 100644
--- a/spec/ruby/core/array/pack/shared/basic.rb
+++ b/spec/ruby/core/array/pack/shared/basic.rb
@@ -27,6 +27,42 @@ describe :array_pack_basic_non_float, shared: true do
[@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
end
+ it "ignores comments in the format string" do
+ # 2 additional directives ('a') are required for the X directive
+ [@obj, @obj, @obj, @obj].pack("aa #{pack_format} # some comment \n#{pack_format}").should be_an_instance_of(String)
+ end
+
+ ruby_version_is ""..."3.2" do
+ it "warns in verbose mode that a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/, verbose: true)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/, verbose: true)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/, verbose: true)
+ end
+ end
+
+ ruby_version_is "3.2"..."3.3" do
+ # https://bugs.ruby-lang.org/issues/19150
+ # NOTE: it's just a plan of the Ruby core team
+ it "warns that a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19150
+ # NOTE: Added this case just to not forget about the decision in the ticket
+ it "raise ArgumentError when a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a R" + pack_format) }.should raise_error(ArgumentError)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should raise_error(ArgumentError)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should raise_error(ArgumentError)
+ end
+ end
+
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
@@ -39,6 +75,10 @@ describe :array_pack_basic_float, shared: true do
[9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
end
+ it "ignores comments in the format string" do
+ [9.3, 4.7].pack(pack_format + "# some comment \n" + pack_format).should be_an_instance_of(String)
+ end
+
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb
index ba174a071a..9510cffed7 100644
--- a/spec/ruby/core/array/pack/shared/float.rb
+++ b/spec/ruby/core/array/pack/shared/float.rb
@@ -25,8 +25,18 @@ describe :array_pack_float_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -74,6 +84,11 @@ describe :array_pack_float_be, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "A\x00\x00\x00"
+ [bignum_value].pack(pack_format).should == "_\x80\x00\x00"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "A\x00\x00\x00"
end
it "raises a TypeError if passed a String representation of a floating point number" do
@@ -88,8 +103,18 @@ describe :array_pack_float_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -129,6 +154,11 @@ describe :array_pack_double_le, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@"
+ [bignum_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xF0C"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00 @"
end
it "raises a TypeError if passed a String representation of a floating point number" do
@@ -143,8 +173,18 @@ describe :array_pack_double_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -202,8 +242,18 @@ describe :array_pack_double_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb
index 6592f85022..d3ce9b5792 100644
--- a/spec/ruby/core/array/pack/shared/integer.rb
+++ b/spec/ruby/core/array/pack/shared/integer.rb
@@ -41,9 +41,19 @@ describe :array_pack_16bit_le, shared: true do
str.should == "\x78\x65\xcd\xab\x21\x43"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\xcd\xab"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x78\x65\xcd\xab"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -93,9 +103,19 @@ describe :array_pack_16bit_be, shared: true do
str.should == "\x65\x78\xab\xcd\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x65\x78\xab\xcd"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x65\x78\xab\xcd"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -145,9 +165,19 @@ describe :array_pack_32bit_le, shared: true do
str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -197,9 +227,19 @@ describe :array_pack_32bit_be, shared: true do
str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -309,9 +349,19 @@ describe :array_pack_64bit_le, shared: true do
str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
@@ -369,9 +419,19 @@ describe :array_pack_64bit_be, shared: true do
str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb
index 7c36ba4a32..545e215e64 100644
--- a/spec/ruby/core/array/pack/shared/numeric_basic.rb
+++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb
@@ -37,8 +37,14 @@ describe :array_pack_float, shared: true do
-> { ["a"].pack(pack_format) }.should raise_error(TypeError)
end
- it "raises a TypeError when the object does not respond to #to_f" do
- obj = mock('not an float')
+ it "raises a TypeError when the object is not Numeric" do
+ obj = Object.new
+ -> { [obj].pack(pack_format) }.should raise_error(TypeError, /can't convert Object into Float/)
+ end
+
+ it "raises a TypeError when the Numeric object does not respond to #to_f" do
+ klass = Class.new(Numeric)
+ obj = klass.new
-> { [obj].pack(pack_format) }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb
index dd0f8b38aa..130c447bb7 100644
--- a/spec/ruby/core/array/pack/shared/unicode.rb
+++ b/spec/ruby/core/array/pack/shared/unicode.rb
@@ -67,8 +67,18 @@ describe :array_pack_unicode, shared: true do
-> { [obj].pack("U") }.should raise_error(TypeError)
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("U\x00U")
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb
index 439fa02198..e241d1519c 100644
--- a/spec/ruby/core/array/pack/w_spec.rb
+++ b/spec/ruby/core/array/pack/w_spec.rb
@@ -24,8 +24,18 @@ describe "Array#pack with format 'w'" do
[obj].pack("w").should == "\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ ruby_version_is ""..."3.3" do
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("w\x00w")
+ }.should raise_error(ArgumentError, /unknown pack directive/)
+ end
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb
index fc82e19e2a..4941e098f6 100644
--- a/spec/ruby/core/array/shared/unshift.rb
+++ b/spec/ruby/core/array/shared/unshift.rb
@@ -22,6 +22,11 @@ describe :array_unshift, shared: true do
a.should == [3, 4]
end
+ it "returns self" do
+ a = [1, 2, 3]
+ a.send(@method, "a").should.equal?(a)
+ end
+
it "quietly ignores unshifting nothing" do
[].send(@method).should == []
end
@@ -43,4 +48,17 @@ describe :array_unshift, shared: true do
it "raises a FrozenError on a frozen array when the array would not be modified" do
-> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError)
end
+
+ # https://github.com/oracle/truffleruby/issues/2772
+ it "doesn't rely on Array#[]= so it can be overridden" do
+ subclass = Class.new(Array) do
+ def []=(*)
+ raise "[]= is called"
+ end
+ end
+
+ array = subclass.new
+ array.send(@method, 1)
+ array.should == [1]
+ end
end
diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb
index 2c6fd16947..e85bbee400 100644
--- a/spec/ruby/core/array/values_at_spec.rb
+++ b/spec/ruby/core/array/values_at_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Should be synchronized with core/struct/values_at_spec.rb
describe "Array#values_at" do
it "returns an array of elements at the indexes when passed indexes" do
[1, 2, 3, 4, 5].values_at().should == []
diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb
index af4013debe..2a0f64cb49 100644
--- a/spec/ruby/core/array/zip_spec.rb
+++ b/spec/ruby/core/array/zip_spec.rb
@@ -62,4 +62,10 @@ describe "Array#zip" do
it "does not return subclass instance on Array subclasses" do
ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array)
end
+
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { [1, 2, 3].zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { [1, 2, 3].zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { [1, 2, 3].zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)")
+ end
end
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
index b6a146095d..350b08a30e 100644
--- a/spec/ruby/core/basicobject/instance_eval_spec.rb
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -20,12 +20,18 @@ describe "BasicObject#instance_eval" do
a.instance_eval('self').equal?(a).should be_true
end
- it "expects a block with no arguments" do
- -> { "hola".instance_eval }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when no arguments and no block are given" do
+ -> { "hola".instance_eval }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
- it "takes no arguments with a block" do
- -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when a block and normal arguments are given" do
+ -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ end
+
+ it "raises an ArgumentError when more than 3 arguments are given" do
+ -> {
+ "hola".instance_eval("1 + 1", "some file", 0, "bogus")
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "yields the object to the block" do
@@ -185,4 +191,58 @@ end
x.should == :value
end
+
+ it "converts string argument with #to_str method" do
+ source_code = Object.new
+ def source_code.to_str() "1" end
+
+ a = BasicObject.new
+ a.instance_eval(source_code).should == 1
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ source_code = Object.new
+ def source_code.to_str() :symbol end
+
+ a = BasicObject.new
+ -> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
+ it "converts filename argument with #to_str method" do
+ filename = Object.new
+ def filename.to_str() "file.rb" end
+
+ err = begin
+ Object.new.instance_eval("raise", filename)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[0].should == "file.rb"
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ filename = Object.new
+ def filename.to_str() :symbol end
+
+ -> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
+ it "converts lineno argument with #to_int method" do
+ lineno = Object.new
+ def lineno.to_int() 15 end
+
+ err = begin
+ Object.new.instance_eval("raise", "file.rb", lineno)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[1].should == "15"
+ end
+
+ it "raises ArgumentError if returned value is not Integer" do
+ lineno = Object.new
+ def lineno.to_int() :symbol end
+
+ -> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/)
+ end
end
diff --git a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
index b0b86030e2..1960f5721f 100644
--- a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
+++ b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
@@ -34,12 +34,6 @@ describe "RUBY_PLATFORM" do
it "is a String" do
RUBY_PLATFORM.should be_kind_of(String)
end
-
- platform_is :darwin do
- it 'ends with the build time kernel major version on darwin' do
- RUBY_PLATFORM.should =~ /-darwin\d+$/
- end
- end
end
describe "RUBY_RELEASE_DATE" do
diff --git a/spec/ruby/core/class/attached_object_spec.rb b/spec/ruby/core/class/attached_object_spec.rb
new file mode 100644
index 0000000000..115d5fa563
--- /dev/null
+++ b/spec/ruby/core/class/attached_object_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '3.2' do
+ describe "Class#attached_object" do
+ it "returns the object that is attached to a singleton class" do
+ a = Class.new
+
+ a_obj = a.new
+ a_obj.singleton_class.attached_object.should == a_obj
+ end
+
+ it "returns the class object that is attached to a class's singleton class" do
+ a = Class.new
+ singleton_class = (class << a; self; end)
+
+ singleton_class.attached_object.should == a
+ end
+
+ it "raises TypeError if the class is not a singleton class" do
+ a = Class.new
+
+ -> { a.attached_object }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError for special singleton classes" do
+ -> { nil.singleton_class.attached_object }.should raise_error(TypeError)
+ -> { true.singleton_class.attached_object }.should raise_error(TypeError)
+ -> { false.singleton_class.attached_object }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/class/subclasses_spec.rb b/spec/ruby/core/class/subclasses_spec.rb
index ddbcfb02c0..a16b934d4f 100644
--- a/spec/ruby/core/class/subclasses_spec.rb
+++ b/spec/ruby/core/class/subclasses_spec.rb
@@ -31,6 +31,28 @@ ruby_version_is '3.1' do
ModuleSpecs::Parent.subclasses.should == ModuleSpecs::Parent.subclasses.uniq
end
+ it "works when creating subclasses concurrently" do
+ t = 16
+ n = 1000
+ go = false
+ superclass = Class.new
+
+ threads = t.times.map do
+ Thread.new do
+ Thread.pass until go
+ n.times.map do
+ Class.new(superclass)
+ end
+ end
+ end
+
+ go = true
+ classes = threads.map(&:value)
+
+ superclass.subclasses.size.should == t * n
+ superclass.subclasses.each { |c| c.should be_kind_of(Class) }
+ end
+
def assert_subclasses(mod,subclasses)
mod.subclasses.sort_by(&:inspect).should == subclasses.sort_by(&:inspect)
end
diff --git a/spec/ruby/core/complex/polar_spec.rb b/spec/ruby/core/complex/polar_spec.rb
index 2a5d8ebd69..3bb3751bc6 100644
--- a/spec/ruby/core/complex/polar_spec.rb
+++ b/spec/ruby/core/complex/polar_spec.rb
@@ -10,6 +10,22 @@ describe "Complex.polar" do
->{ Complex.polar(nil) }.should raise_error(TypeError)
->{ Complex.polar(nil, nil) }.should raise_error(TypeError)
end
+
+ ruby_bug "#19004", ""..."3.2" do
+ it "computes the real values of the real & imaginary parts from the polar form" do
+ a = Complex.polar(1.0+0.0i, Math::PI/2+0.0i)
+ a.real.should be_close(0.0, TOLERANCE)
+ a.imag.should be_close(1.0, TOLERANCE)
+ a.real.real?.should be_true
+ a.imag.real?.should be_true
+
+ b = Complex.polar(1+0.0i)
+ b.real.should be_close(1.0, TOLERANCE)
+ b.imag.should be_close(0.0, TOLERANCE)
+ b.real.real?.should be_true
+ b.imag.real?.should be_true
+ end
+ end
end
describe "Complex#polar" do
diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb
index 1d469f9237..d9d55b50f9 100644
--- a/spec/ruby/core/data/constants_spec.rb
+++ b/spec/ruby/core/data/constants_spec.rb
@@ -14,10 +14,22 @@ ruby_version_is ''...'3.0' do
end
end
-ruby_version_is '3.0' do
+ruby_version_is '3.0'...'3.2' do
describe "Data" do
it "does not exist anymore" do
Object.should_not have_constant(:Data)
end
end
end
+
+ruby_version_is '3.2' do
+ describe "Data" do
+ it "is a new constant" do
+ Data.superclass.should == Object
+ end
+
+ it "is not deprecated" do
+ -> { Data }.should_not complain
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb
index a8d6e69c44..087f46b331 100644
--- a/spec/ruby/core/dir/fixtures/common.rb
+++ b/spec/ruby/core/dir/fixtures/common.rb
@@ -82,6 +82,7 @@ module DirSpecs
special/test{1}/file[1]
special/{}/special
+ special/test\ +()[]{}/hello_world.erb
]
platform_is_not :windows do
diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb
index 43dac73eee..06b52b90fb 100644
--- a/spec/ruby/core/dir/glob_spec.rb
+++ b/spec/ruby/core/dir/glob_spec.rb
@@ -79,6 +79,7 @@ describe "Dir.glob" do
nested/
nested/.dotsubir/
special/
+ special/test\ +()[]{}/
special/test{1}/
special/{}/
subdir_one/
@@ -130,6 +131,7 @@ describe "Dir.glob" do
./nested/
./nested/.dotsubir/
./special/
+ ./special/test\ +()[]{}/
./special/test{1}/
./special/{}/
./subdir_one/
diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb
index 8377f1dc97..bbe347ba9e 100644
--- a/spec/ruby/core/dir/home_spec.rb
+++ b/spec/ruby/core/dir/home_spec.rb
@@ -19,6 +19,45 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home.should_not.frozen?
end
+
+ it "returns a string with the filesystem encoding" do
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+
+ platform_is_not :windows do
+ it "works even if HOME is unset" do
+ ENV.delete('HOME')
+ Dir.home.should.start_with?('/')
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+ end
+
+ platform_is :windows do
+ ruby_version_is "3.2" do
+ it "returns the home directory with forward slashs and as UTF-8" do
+ ENV['HOME'] = "C:\\rubyspäc\\home"
+ home = Dir.home
+ home.should == "C:/rubyspäc/home"
+ home.encoding.should == Encoding::UTF_8
+ end
+ end
+
+ it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do
+ old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')]
+
+ Dir.home.should == old_dirs[1].gsub("\\", "/")
+ ENV['HOMEDRIVE'] = "C:"
+ ENV['HOMEPATH'] = "\\rubyspec\\home1"
+ Dir.home.should == "C:/rubyspec/home1"
+ ENV['USERPROFILE'] = "C:\\rubyspec\\home2"
+ # https://bugs.ruby-lang.org/issues/19244
+ # Dir.home.should == "C:/rubyspec/home2"
+ ENV['HOME'] = "C:\\rubyspec\\home3"
+ Dir.home.should == "C:/rubyspec/home3"
+ ensure
+ ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs
+ end
+ end
end
describe "when called with the current user name" do
@@ -37,6 +76,10 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home(ENV['USER']).should_not.frozen?
end
+
+ it "returns a string with the filesystem encoding" do
+ Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem")
+ end
end
it "raises an ArgumentError if the named user doesn't exist" do
diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb
index 0ed28f5a99..076ec19dd9 100644
--- a/spec/ruby/core/dir/mkdir_spec.rb
+++ b/spec/ruby/core/dir/mkdir_spec.rb
@@ -46,7 +46,7 @@ describe "Dir.mkdir" do
end
end
- it "calls #to_path on non-String arguments" do
+ it "calls #to_path on non-String path arguments" do
DirSpecs.clear_dirs
p = mock('path')
p.should_receive(:to_path).and_return(DirSpecs.mock_dir('nonexisting'))
@@ -54,6 +54,22 @@ describe "Dir.mkdir" do
DirSpecs.clear_dirs
end
+ it "calls #to_int on non-Integer permissions argument" do
+ DirSpecs.clear_dirs
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = mock('permissions')
+ permissions.should_receive(:to_int).and_return(0666)
+ Dir.mkdir(path, permissions)
+ DirSpecs.clear_dirs
+ end
+
+ it "raises TypeError if non-Integer permissions argument does not have #to_int method" do
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = Object.new
+
+ -> { Dir.mkdir(path, permissions) }.should raise_error(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
it "raises a SystemCallError if any of the directories in the path before the last does not exist" do
-> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError)
end
diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb
index b14a433670..8c0599fe3f 100644
--- a/spec/ruby/core/dir/shared/chroot.rb
+++ b/spec/ruby/core/dir/shared/chroot.rb
@@ -3,7 +3,7 @@ describe :dir_chroot_as_root, shared: true do
DirSpecs.create_mock_dirs
@real_root = "../" * (File.dirname(__FILE__).count('/') - 1)
- @ref_dir = File.join("/", Dir.new('/').entries.first)
+ @ref_dir = File.join("/", File.basename(Dir["/*"].first))
end
after :all do
@@ -14,10 +14,13 @@ describe :dir_chroot_as_root, shared: true do
DirSpecs.delete_mock_dirs
end
+ # Pending until https://github.com/ruby/ruby/runs/8075149420 is fixed
+ compilations_ci = ENV["GITHUB_WORKFLOW"] == "Compilations"
+
it "can be used to change the process' root directory" do
-> { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error
File.should.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "returns 0 if successful" do
Dir.send(@method, '/').should == 0
@@ -31,7 +34,7 @@ describe :dir_chroot_as_root, shared: true do
Dir.send(@method, @real_root)
File.should.exist?(@ref_dir)
File.should_not.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "calls #to_path on non-String argument" do
p = mock('path')
diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb
index 60d4a8c97a..33b2828c27 100644
--- a/spec/ruby/core/dir/shared/glob.rb
+++ b/spec/ruby/core/dir/shared/glob.rb
@@ -111,6 +111,10 @@ describe :dir_glob, shared: true do
it "matches files with backslashes in their name" do
Dir.glob('special/\\\\{a,b}').should == ['special/\a']
end
+
+ it "matches directory with special characters in their name in complex patterns" do
+ Dir.glob("special/test +()\\[\\]\\{\\}/hello_world{.{en},}{.{html},}{+{phone},}{.{erb},}").should == ['special/test +()[]{}/hello_world.erb']
+ end
end
it "matches regexp special ^" do
@@ -225,6 +229,7 @@ describe :dir_glob, shared: true do
dir/
nested/
special/
+ special/test\ +()[]{}/
special/test{1}/
special/{}/
subdir_one/
diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb
index 45727a5c0d..848415eeb4 100644
--- a/spec/ruby/core/encoding/replicate_spec.rb
+++ b/spec/ruby/core/encoding/replicate_spec.rb
@@ -2,66 +2,74 @@
require_relative '../../spec_helper'
describe "Encoding#replicate" do
- before :all do
- @i = 0
- end
+ ruby_version_is ""..."3.3" do
+ before :all do
+ @i = 0
+ end
- before :each do
- @i += 1
- @prefix = "RS#{@i}"
- end
+ before :each do
+ @i += 1
+ @prefix = "RS#{@i}"
+ end
- it "returns a replica of ASCII" do
- name = @prefix + '-ASCII'
- e = Encoding::ASCII.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of ASCII" do
+ name = @prefix + '-ASCII'
+ e = suppress_warning { Encoding::ASCII.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of UTF-8" do
- name = @prefix + 'UTF-8'
- e = Encoding::UTF_8.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of UTF-8" do
+ name = @prefix + 'UTF-8'
+ e = suppress_warning { Encoding::UTF_8.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\u3042".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_true
+ "\u3042".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of UTF-16BE" do
- name = @prefix + 'UTF-16-BE'
- e = Encoding::UTF_16BE.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ it "returns a replica of UTF-16BE" do
+ name = @prefix + 'UTF-16-BE'
+ e = suppress_warning { Encoding::UTF_16BE.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_false
- "\x30\x42".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
+ "a".force_encoding(e).valid_encoding?.should be_false
+ "\x30\x42".force_encoding(e).valid_encoding?.should be_true
+ "\x80".force_encoding(e).valid_encoding?.should be_false
+ end
- it "returns a replica of ISO-2022-JP" do
- name = @prefix + 'ISO-2022-JP'
- e = Encoding::ISO_2022_JP.replicate(name)
- Encoding.find(name).should == e
+ it "returns a replica of ISO-2022-JP" do
+ name = @prefix + 'ISO-2022-JP'
+ e = suppress_warning { Encoding::ISO_2022_JP.replicate(name) }
+ Encoding.find(name).should == e
- e.name.should == name
- e.dummy?.should be_true
- end
+ e.name.should == name
+ e.dummy?.should be_true
+ end
- # NOTE: it's unclear of the value of this (for the complexity cost of it),
- # but it is the current CRuby behavior.
- it "can be associated with a String" do
- name = @prefix + '-US-ASCII'
- e = Encoding::US_ASCII.replicate(name)
- e.name.should == name
- Encoding.find(name).should == e
+ # NOTE: it's unclear of the value of this (for the complexity cost of it),
+ # but it is the current CRuby behavior.
+ it "can be associated with a String" do
+ name = @prefix + '-US-ASCII'
+ e = suppress_warning { Encoding::US_ASCII.replicate(name) }
+ e.name.should == name
+ Encoding.find(name).should == e
+
+ s = "abc".force_encoding(e)
+ s.encoding.should == e
+ s.encoding.name.should == name
+ end
+ end
- s = "abc".force_encoding(e)
- s.encoding.should == e
- s.encoding.name.should == name
+ ruby_version_is "3.3" do
+ it "has been removed" do
+ Encoding::US_ASCII.should_not.respond_to?(:replicate, true)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/each_cons_spec.rb b/spec/ruby/core/enumerable/each_cons_spec.rb
index ba658203a2..8fb31fb925 100644
--- a/spec/ruby/core/enumerable/each_cons_spec.rb
+++ b/spec/ruby/core/enumerable/each_cons_spec.rb
@@ -56,6 +56,12 @@ describe "Enumerable#each_cons" do
multi.each_cons(2).to_a.should == [[[1, 2], [3, 4, 5]], [[3, 4, 5], [6, 7, 8, 9]]]
end
+ ruby_version_is "3.1" do
+ it "returns self when a block is given" do
+ @enum.each_cons(3){}.should == @enum
+ end
+ end
+
describe "when no block is given" do
it "returns an enumerator" do
e = @enum.each_cons(3)
diff --git a/spec/ruby/core/enumerable/each_slice_spec.rb b/spec/ruby/core/enumerable/each_slice_spec.rb
index 2ea89f5e72..a57a1dba81 100644
--- a/spec/ruby/core/enumerable/each_slice_spec.rb
+++ b/spec/ruby/core/enumerable/each_slice_spec.rb
@@ -57,6 +57,12 @@ describe "Enumerable#each_slice" do
e.to_a.should == @sliced
end
+ ruby_version_is "3.1" do
+ it "returns self when a block is given" do
+ @enum.each_slice(3){}.should == @enum
+ end
+ end
+
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
multi.each_slice(2).to_a.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]]
diff --git a/spec/ruby/core/enumerable/sum_spec.rb b/spec/ruby/core/enumerable/sum_spec.rb
index 4a978794e5..fc173e4173 100644
--- a/spec/ruby/core/enumerable/sum_spec.rb
+++ b/spec/ruby/core/enumerable/sum_spec.rb
@@ -22,8 +22,21 @@ describe 'Enumerable#sum' do
@enum.sum.should == 5/3r
end
- it 'takes a block to transform the elements' do
- @enum.sum { |element| element * 2 }.should == 10/3r
+ context 'with a block' do
+ it 'transforms the elements' do
+ @enum.sum { |element| element * 2 }.should == 10/3r
+ end
+
+ it 'does not destructure array elements' do
+ class << @enum
+ def each
+ yield [1,2]
+ yield [3]
+ end
+ end
+
+ @enum.sum(&:last).should == 5
+ end
end
# https://bugs.ruby-lang.org/issues/12217
diff --git a/spec/ruby/core/enumerable/zip_spec.rb b/spec/ruby/core/enumerable/zip_spec.rb
index 9ec15aa030..ab148f2a6e 100644
--- a/spec/ruby/core/enumerable/zip_spec.rb
+++ b/spec/ruby/core/enumerable/zip_spec.rb
@@ -38,4 +38,9 @@ describe "Enumerable#zip" do
multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]]
end
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)")
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/compact_spec.rb b/spec/ruby/core/enumerator/lazy/compact_spec.rb
new file mode 100644
index 0000000000..80b6f9481d
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/compact_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is '3.1' do
+ describe "Enumerator::Lazy#compact" do
+ it 'returns array without nil elements' do
+ arr = [1, nil, 3, false, 5].to_enum.lazy.compact
+ arr.should be_an_instance_of(Enumerator::Lazy)
+ arr.force.should == [1, 3, false, 5]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/lazy_spec.rb b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
index 683dfb81d7..0fb104e25a 100644
--- a/spec/ruby/core/enumerator/lazy/lazy_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
@@ -30,13 +30,3 @@ describe "Enumerator::Lazy#lazy" do
lazy.lazy.should equal(lazy)
end
end
-
-ruby_version_is '3.1' do
- describe "Enumerator::Lazy#compact" do
- it 'returns array without nil elements' do
- arr = [1, nil, 3, false, 5].to_enum.lazy.compact
- arr.should be_an_instance_of(Enumerator::Lazy)
- arr.force.should == [1, 3, false, 5]
- end
- end
-end
diff --git a/spec/ruby/core/env/shared/update.rb b/spec/ruby/core/env/shared/update.rb
index 3101f9c561..7d4799955b 100644
--- a/spec/ruby/core/env/shared/update.rb
+++ b/spec/ruby/core/env/shared/update.rb
@@ -17,10 +17,9 @@ describe :env_update, shared: true do
ruby_version_is "3.2" do
it "adds the multiple parameter hashes to ENV, returning ENV" do
- ENV.send(@method, {"foo" => "0", "bar" => "1"}, {"baz" => "2"}).should equal(ENV)
- ENV["foo"].should == "0"
- ENV["bar"].should == "1"
- ENV["baz"].should == "2"
+ ENV.send(@method, {"foo" => "multi1"}, {"bar" => "multi2"}).should equal(ENV)
+ ENV["foo"].should == "multi1"
+ ENV["bar"].should == "multi2"
end
end
diff --git a/spec/ruby/core/false/case_compare_spec.rb b/spec/ruby/core/false/case_compare_spec.rb
new file mode 100644
index 0000000000..0bd0ab44ae
--- /dev/null
+++ b/spec/ruby/core/false/case_compare_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#===" do
+ it "returns true for false" do
+ (false === false).should == true
+ end
+
+ it "returns false for non-false object" do
+ (false === 0).should == false
+ (false === "").should == false
+ (false === Object).should == false
+ (false === nil).should == false
+ end
+end
diff --git a/spec/ruby/core/fiber/blocking_spec.rb b/spec/ruby/core/fiber/blocking_spec.rb
index 5ae5fbd577..eeee5a71c1 100644
--- a/spec/ruby/core/fiber/blocking_spec.rb
+++ b/spec/ruby/core/fiber/blocking_spec.rb
@@ -60,3 +60,20 @@ ruby_version_is "3.0" do
end
end
end
+
+ruby_version_is "3.2" do
+ describe "Fiber.blocking" do
+ context "when fiber is non-blocking" do
+ it "can become blocking" do
+ fiber = Fiber.new(blocking: false) do
+ Fiber.blocking do |f|
+ f.blocking? ? :blocking : :non_blocking
+ end
+ end
+
+ blocking = fiber.resume
+ blocking.should == :blocking
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb
new file mode 100644
index 0000000000..e2bf6da04c
--- /dev/null
+++ b/spec/ruby/core/fiber/storage_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+
+require 'fiber'
+
+describe "Fiber.new(storage:)" do
+ ruby_version_is "3.2" do
+ it "creates a Fiber with the given storage" do
+ storage = {life: 42}
+ fiber = Fiber.new(storage: storage) { Fiber.current.storage }
+ fiber.resume.should == storage
+ end
+
+ it "creates a fiber with lazily initialized storage" do
+ Fiber.new(storage: nil) { Fiber.current.storage }.resume.should == {}
+ end
+
+ it "creates a fiber by inheriting the storage of the parent fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Fiber.new { Fiber.current.storage }.resume
+ end
+ fiber.resume.should == {life: 42}
+ end
+
+ it "cannot create a fiber with non-hash storage" do
+ -> { Fiber.new(storage: 42) {} }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "Fiber#storage=" do
+ ruby_version_is "3.2" do
+ it "can clear the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) {
+ Fiber.current.storage = nil
+ Fiber.current.storage
+ }
+ fiber.resume.should == {}
+ end
+
+ it "can set the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) {
+ Fiber.current.storage = {life: 43}
+ Fiber.current.storage
+ }
+ fiber.resume.should == {life: 43}
+ end
+
+ it "can't set the storage of the fiber to non-hash" do
+ -> { Fiber.current.storage = 42 }.should raise_error(TypeError)
+ end
+
+ it "can't set the storage of the fiber to a frozen hash" do
+ -> { Fiber.current.storage = {life: 43}.freeze }.should raise_error(FrozenError)
+ end
+
+ it "can't set the storage of the fiber to a hash with non-symbol keys" do
+ -> { Fiber.current.storage = {life: 43, Object.new => 44} }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "Fiber.[]" do
+ ruby_version_is "3.2" do
+ it "returns the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] }.resume.should == 42
+ end
+
+ it "returns nil if the key is not present in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] }.resume.should be_nil
+ end
+
+ it "returns nil if the current fiber has no storage" do
+ Fiber.new { Fiber[:life] }.resume.should be_nil
+ end
+ end
+
+ ruby_version_is "3.2.3" do
+ it "can use dynamically defined keys" do
+ key = :"#{self.class.name}#.#{self.object_id}"
+ Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42
+ end
+
+ it "can't use invalid keys" do
+ invalid_keys = [Object.new, "Foo", 12]
+ invalid_keys.each do |key|
+ -> { Fiber[key] }.should raise_error(TypeError)
+ end
+ end
+ end
+end
+
+describe "Fiber.[]=" do
+ ruby_version_is "3.2" do
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] = 43; Fiber[:death] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+ end
+end
+
+describe "Thread.new" do
+ ruby_version_is "3.2" do
+ it "creates a thread with the storage of the current fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Thread.new { Fiber.current.storage }.value
+ end
+ fiber.resume.should == {life: 42}
+ end
+ end
+end
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
index cef07ba010..3df258016c 100644
--- a/spec/ruby/core/file/atime_spec.rb
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -15,11 +15,11 @@ describe "File.atime" do
File.atime(@file).should be_kind_of(Time)
end
- platform_is :linux, :windows do
- platform_is_not :"powerpc64le-linux" do # https://bugs.ruby-lang.org/issues/17926
+ platform_is :linux do
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed.
it "returns the last access time for the named file with microseconds" do
- supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10)
+ supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
if supports_subseconds != 0
expected_time = Time.at(Time.now.to_i + 0.123456)
File.utime expected_time, 0, @file
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
index b16eb13c1e..9b7ab272ff 100644
--- a/spec/ruby/core/file/ctime_spec.rb
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -14,9 +14,9 @@ describe "File.ctime" do
File.ctime(@file).should be_kind_of(Time)
end
- platform_is :linux, :windows do
+ platform_is :linux do
it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do
- supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10)
+ supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
if supports_subseconds != 0
File.ctime(__FILE__).usec.should > 0
else
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
index 8d47d3021a..6c43265a2c 100644
--- a/spec/ruby/core/file/mtime_spec.rb
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -15,15 +15,17 @@ describe "File.mtime" do
File.mtime(@filename).should be_close(@mtime, TIME_TOLERANCE)
end
- platform_is :linux, :windows do
- it "returns the modification Time of the file with microseconds" do
- supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10)
- if supports_subseconds != 0
- expected_time = Time.at(Time.now.to_i + 0.123456)
- File.utime 0, expected_time, @filename
- File.mtime(@filename).usec.should == expected_time.usec
- else
- File.mtime(__FILE__).usec.should == 0
+ platform_is :linux do
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
+ it "returns the modification Time of the file with microseconds" do
+ supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime 0, expected_time, @filename
+ File.mtime(@filename).usec.should == expected_time.usec
+ else
+ File.mtime(__FILE__).usec.should == 0
+ end
end
end
end
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
index 0a5abe33f0..ee8109ba05 100644
--- a/spec/ruby/core/file/shared/path.rb
+++ b/spec/ruby/core/file/shared/path.rb
@@ -78,13 +78,15 @@ describe :file_path, shared: true do
rm_r @dir
end
- it "raises IOError if file was opened with File::TMPFILE" do
- begin
- File.open(@dir, File::RDWR | File::TMPFILE) do |f|
- -> { f.send(@method) }.should raise_error(IOError)
+ ruby_version_is ""..."3.1" do
+ it "raises IOError if file was opened with File::TMPFILE" do
+ begin
+ File.open(@dir, File::RDWR | File::TMPFILE) do |f|
+ -> { f.send(@method) }.should raise_error(IOError)
+ end
+ rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
+ skip "no support from the filesystem"
end
- rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
- skip "no support from the filesystem"
end
end
end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
index a191e29240..cf7a0aec20 100644
--- a/spec/ruby/core/file/utime_spec.rb
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -19,18 +19,20 @@ describe "File.utime" do
rm_r @file1, @file2
end
- it "sets the access and modification time of each file" do
- File.utime(@atime, @mtime, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ platform_is_not :windows do
+ it "sets the access and modification time of each file" do
+ File.utime(@atime, @mtime, @file1, @file2)
+ if @time_is_float
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
end
end
@@ -83,17 +85,19 @@ describe "File.utime" do
platform_is :linux do
platform_is wordsize: 64 do
- it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19)" do
+ it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
+ # Amazon Linux 2023 returns 2486-07-02 in this example
+ # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.utime(time, time, @file1)
- [559444, 2446, 2038].should.include? File.atime(@file1).year
- [559444, 2446, 2038].should.include? File.mtime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb
index 53e7ec332a..1373b3a1fb 100644
--- a/spec/ruby/core/float/comparison_spec.rb
+++ b/spec/ruby/core/float/comparison_spec.rb
@@ -7,9 +7,25 @@ describe "Float#<=>" do
((bignum_value*1.1) <=> bignum_value).should == 1
end
- it "returns nil when either argument is NaN" do
- (nan_value <=> 71.2).should be_nil
- (1771.176 <=> nan_value).should be_nil
+ it "returns nil if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <=> n).should == nil
+ (n <=> nan_value).should == nil
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <=> n).should == 1
+ (n <=> infinity_value).should == -1
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <=> n).should == -1
+ (n <=> -infinity_value).should == 1
+ }
end
it "returns nil when the given argument is not a Float" do
@@ -49,21 +65,10 @@ describe "Float#<=>" do
}.should raise_error(TypeError, "coerce must return [x, y]")
end
- # The 4 tests below are taken from matz's revision 23730 for Ruby trunk
- #
- it "returns 1 when self is Infinity and other is an Integer" do
+ it "returns the correct result when one side is infinite" do
(infinity_value <=> Float::MAX.to_i*2).should == 1
- end
-
- it "returns -1 when self is negative and other is Infinity" do
(-Float::MAX.to_i*2 <=> infinity_value).should == -1
- end
-
- it "returns -1 when self is -Infinity and other is negative" do
(-infinity_value <=> -Float::MAX.to_i*2).should == -1
- end
-
- it "returns 1 when self is negative and other is -Infinity" do
(-Float::MAX.to_i*2 <=> -infinity_value).should == 1
end
diff --git a/spec/ruby/core/float/divmod_spec.rb b/spec/ruby/core/float/divmod_spec.rb
index 523217ac1f..dad45a9b89 100644
--- a/spec/ruby/core/float/divmod_spec.rb
+++ b/spec/ruby/core/float/divmod_spec.rb
@@ -23,7 +23,7 @@ describe "Float#divmod" do
# Behaviour established as correct in r23953
it "raises a FloatDomainError if other is NaN" do
- -> { 1.divmod(nan_value) }.should raise_error(FloatDomainError)
+ -> { 1.0.divmod(nan_value) }.should raise_error(FloatDomainError)
end
# Behaviour established as correct in r23953
diff --git a/spec/ruby/core/float/gt_spec.rb b/spec/ruby/core/float/gt_spec.rb
index 0d73f1c3df..33078e07ce 100644
--- a/spec/ruby/core/float/gt_spec.rb
+++ b/spec/ruby/core/float/gt_spec.rb
@@ -14,4 +14,25 @@ describe "Float#>" do
-> { 5.0 > "4" }.should raise_error(ArgumentError)
-> { 5.0 > mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value > n).should == false
+ (n > nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value > n).should == true
+ (n > infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value > n).should == false
+ (n > -infinity_value).should == true
+ }
+ end
end
diff --git a/spec/ruby/core/float/gte_spec.rb b/spec/ruby/core/float/gte_spec.rb
index 98ec60b70b..44c0a81b43 100644
--- a/spec/ruby/core/float/gte_spec.rb
+++ b/spec/ruby/core/float/gte_spec.rb
@@ -14,4 +14,25 @@ describe "Float#>=" do
-> { 5.0 >= "4" }.should raise_error(ArgumentError)
-> { 5.0 >= mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value >= n).should == false
+ (n >= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value >= n).should == true
+ (n >= infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value >= n).should == false
+ (n >= -infinity_value).should == true
+ }
+ end
end
diff --git a/spec/ruby/core/float/lt_spec.rb b/spec/ruby/core/float/lt_spec.rb
index c01b6e0e02..94dcfc42f8 100644
--- a/spec/ruby/core/float/lt_spec.rb
+++ b/spec/ruby/core/float/lt_spec.rb
@@ -14,4 +14,25 @@ describe "Float#<" do
-> { 5.0 < "4" }.should raise_error(ArgumentError)
-> { 5.0 < mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value < n).should == false
+ (n < nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value < n).should == false
+ (n < infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value < n).should == true
+ (n < -infinity_value).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/lte_spec.rb b/spec/ruby/core/float/lte_spec.rb
index 66f2ddc2c7..7b5a86ee76 100644
--- a/spec/ruby/core/float/lte_spec.rb
+++ b/spec/ruby/core/float/lte_spec.rb
@@ -15,4 +15,25 @@ describe "Float#<=" do
-> { 5.0 <= "4" }.should raise_error(ArgumentError)
-> { 5.0 <= mock('x') }.should raise_error(ArgumentError)
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <= n).should == false
+ (n <= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <= n).should == false
+ (n <= infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <= n).should == true
+ (n <= -infinity_value).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb
index 668aa069b5..4d524e1cf2 100644
--- a/spec/ruby/core/float/shared/equal.rb
+++ b/spec/ruby/core/float/shared/equal.rb
@@ -14,4 +14,25 @@ describe :float_equal, shared: true do
1.0.send(@method, x).should == false
2.0.send(@method, x).should == true
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value.send(@method, n)).should == false
+ (n.send(@method, nan_value)).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value.send(@method, n)).should == false
+ (n.send(@method, infinity_value)).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ ((-infinity_value).send(@method, n)).should == false
+ (n.send(@method, -infinity_value)).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/shared/to_i.rb b/spec/ruby/core/float/shared/to_i.rb
index 960295f095..33b32ca533 100644
--- a/spec/ruby/core/float/shared/to_i.rb
+++ b/spec/ruby/core/float/shared/to_i.rb
@@ -7,4 +7,8 @@ describe :float_to_i, shared: true do
-9223372036854775808.1.send(@method).should eql(-9223372036854775808)
9223372036854775808.1.send(@method).should eql(9223372036854775808)
end
+
+ it "raises a FloatDomainError for NaN" do
+ -> { nan_value.send(@method) }.should raise_error(FloatDomainError)
+ end
end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
index 3649d4d8de..2ccb483120 100644
--- a/spec/ruby/core/hash/hash_spec.rb
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -41,4 +41,13 @@ describe "Hash#hash" do
h.hash.should == {x: [h]}.hash
# Like above, because h.eql?(x: [h])
end
+
+ ruby_version_is "3.1" do
+ it "allows ommiting values" do
+ a = 1
+ b = 2
+
+ eval('{a:, b:}.should == { a: 1, b: 2 }')
+ end
+ end
end
diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb
index 067ab59d93..204a2a101b 100644
--- a/spec/ruby/core/io/fixtures/classes.rb
+++ b/spec/ruby/core/io/fixtures/classes.rb
@@ -7,6 +7,18 @@ module IOSpecs
class SubIO < IO
end
+ class SubIOWithRedefinedNew < IO
+ def self.new(...)
+ ScratchPad << :redefined_new_called
+ super
+ end
+
+ def initialize(...)
+ ScratchPad << :call_original_initialize
+ super
+ end
+ end
+
def self.collector
Proc.new { |x| ScratchPad << x }
end
diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb
index b9f82f8133..d0c91705af 100644
--- a/spec/ruby/core/io/gets_spec.rb
+++ b/spec/ruby/core/io/gets_spec.rb
@@ -213,6 +213,12 @@ describe "IO#gets" do
it "returns empty string when 0 passed as a limit" do
@io.gets(0).should == ""
+ @io.gets(nil, 0).should == ""
+ @io.gets("", 0).should == ""
+ end
+
+ it "does not accept limit that doesn't fit in a C off_t" do
+ -> { @io.gets(2**128) }.should raise_error(RangeError)
end
end
diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb
index 99266ecca1..9a4ad90880 100644
--- a/spec/ruby/core/io/lineno_spec.rb
+++ b/spec/ruby/core/io/lineno_spec.rb
@@ -92,8 +92,13 @@ describe "IO#lineno=" do
@io.lineno.should == 92233
end
- it "raises TypeError on nil argument" do
- -> { @io.lineno = nil }.should raise_error(TypeError)
+ it "raises TypeError if cannot convert argument to Integer implicitly" do
+ -> { @io.lineno = "1" }.should raise_error(TypeError, 'no implicit conversion of String into Integer')
+ -> { @io.lineno = nil }.should raise_error(TypeError, 'no implicit conversion from nil to integer')
+ end
+
+ it "does not accept Integers that don't fit in a C int" do
+ -> { @io.lineno = 2**32 }.should raise_error(RangeError)
end
it "sets the current line number to the given value" do
diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb
index 3597098caf..0ef30991fd 100644
--- a/spec/ruby/core/io/new_spec.rb
+++ b/spec/ruby/core/io/new_spec.rb
@@ -1,6 +1,8 @@
require_relative '../../spec_helper'
require_relative 'shared/new'
+# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+
describe "IO.new" do
it_behaves_like :io_new, :new
end
diff --git a/spec/ruby/core/io/path_spec.rb b/spec/ruby/core/io/path_spec.rb
new file mode 100644
index 0000000000..8145c32f39
--- /dev/null
+++ b/spec/ruby/core/io/path_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "IO#path" do
+ ruby_version_is "3.2" do
+ it "returns the path of the file associated with the IO object" do
+ path = tmp("io_path.txt")
+ File.open(path, "w") do |file|
+ IO.new(file.fileno, path: file.path, autoclose: false).path.should == file.path
+ end
+ ensure
+ File.unlink(path)
+ end
+ end
+end
diff --git a/spec/ruby/core/io/pipe_spec.rb b/spec/ruby/core/io/pipe_spec.rb
index 2f2cf06f4d..aee0d9003f 100644
--- a/spec/ruby/core/io/pipe_spec.rb
+++ b/spec/ruby/core/io/pipe_spec.rb
@@ -25,6 +25,17 @@ describe "IO.pipe" do
@r.should be_an_instance_of(IOSpecs::SubIO)
@w.should be_an_instance_of(IOSpecs::SubIO)
end
+
+ it "does not use IO.new method to create pipes and allows its overriding" do
+ ScratchPad.record []
+
+ # so redefined .new is not called, but original #initialize is
+ @r, @w = IOSpecs::SubIOWithRedefinedNew.pipe
+ ScratchPad.recorded.should == [:call_original_initialize, :call_original_initialize] # called 2 times - for each pipe (r and w)
+
+ @r.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
+ @w.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
+ end
end
describe "IO.pipe" do
diff --git a/spec/ruby/core/io/print_spec.rb b/spec/ruby/core/io/print_spec.rb
index 04e971ef6d..085852024c 100644
--- a/spec/ruby/core/io/print_spec.rb
+++ b/spec/ruby/core/io/print_spec.rb
@@ -3,16 +3,27 @@ require_relative 'fixtures/classes'
describe "IO#print" do
before :each do
- @old_separator = $\
- suppress_warning {$\ = '->'}
+ @old_record_separator = $\
+ @old_field_separator = $,
+ suppress_warning {
+ $\ = '->'
+ $, = '^^'
+ }
@name = tmp("io_print")
end
after :each do
- suppress_warning {$\ = @old_separator}
+ suppress_warning {
+ $\ = @old_record_separator
+ $, = @old_field_separator
+ }
rm_r @name
end
+ it "returns nil" do
+ touch(@name) { |f| f.print.should be_nil }
+ end
+
it "writes $_.to_s followed by $\\ (if any) to the stream if no arguments given" do
o = mock('o')
o.should_receive(:to_s).and_return("mockmockmock")
@@ -38,13 +49,15 @@ describe "IO#print" do
IO.read(@name).should == "hello#{$\}"
end
- it "writes each obj.to_s to the stream and appends $\\ (if any) given multiple objects" do
+ it "writes each obj.to_s to the stream separated by $, (if any) and appends $\\ (if any) given multiple objects" do
o, o2 = Object.new, Object.new
def o.to_s(); 'o'; end
def o2.to_s(); 'o2'; end
- touch(@name) { |f| f.print(o, o2) }
- IO.read(@name).should == "#{o.to_s}#{o2.to_s}#{$\}"
+ suppress_warning {
+ touch(@name) { |f| f.print(o, o2) }
+ }
+ IO.read(@name).should == "#{o.to_s}#{$,}#{o2.to_s}#{$\}"
end
it "raises IOError on closed stream" do
diff --git a/spec/ruby/core/io/read_nonblock_spec.rb b/spec/ruby/core/io/read_nonblock_spec.rb
index e50531d336..a62b75274c 100644
--- a/spec/ruby/core/io/read_nonblock_spec.rb
+++ b/spec/ruby/core/io/read_nonblock_spec.rb
@@ -55,6 +55,27 @@ describe "IO#read_nonblock" do
@read.read_nonblock(4).should == "hell"
end
+ it "reads after ungetc with data in the buffer" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: false
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ @read.read_nonblock(3).should == "foo"
+ @read.read_nonblock(3).should == "bar"
+ end
+
+ it "raises an exception after ungetc with data in the buffer and character conversion enabled" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: true
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ -> { @read.read_nonblock(3).should == "foo" }.should raise_error(IOError)
+ end
+
it "returns less data if that is all that is available" do
@write << "hello"
@read.read_nonblock(10).should == "hello"
@@ -70,6 +91,10 @@ describe "IO#read_nonblock" do
@read.read_nonblock(1).should == "1"
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.read_nonblock(-1) }.should raise_error(ArgumentError)
+ end
+
it "reads into the passed buffer" do
buffer = ""
@write.write("1")
@@ -84,6 +109,21 @@ describe "IO#read_nonblock" do
output.should equal(buffer)
end
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing content"
+ @write.write("hello world")
+ @write.close
+ @read.read_nonblock(11, buffer)
+ buffer.should == "hello world"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = "existing content"
+ @write.close
+ -> { @read.read_nonblock(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
+
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.read_nonblock(5) }.should raise_error(IOError)
end
@@ -96,4 +136,13 @@ describe "IO#read_nonblock" do
-> { @read.read_nonblock(5) }.should raise_error(EOFError)
end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @write.write("abc")
+ @write.close
+ @read.read_nonblock(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
end
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
index 28cab13340..529afbf0ff 100644
--- a/spec/ruby/core/io/read_spec.rb
+++ b/spec/ruby/core/io/read_spec.rb
@@ -270,6 +270,13 @@ describe "IO#read" do
@io.read(nil, buf).should equal buf
end
+ it "returns the given buffer when there is nothing to read" do
+ buf = ""
+
+ @io.read
+ @io.read(nil, buf).should equal buf
+ end
+
it "coerces the second argument to string and uses it as a buffer" do
buf = "ABCDE"
obj = mock("buff")
@@ -312,6 +319,9 @@ describe "IO#read" do
-> { IOSpecs.closed_io.read }.should raise_error(IOError)
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @io.read(-1) }.should raise_error(ArgumentError)
+ end
platform_is_not :windows do
it "raises IOError when stream is closed by another thread" do
@@ -392,13 +402,6 @@ describe "IO#read in binary mode" do
xE2 = [226].pack('C*')
result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
end
-
- it "does not transcode file contents when an internal encoding is specified" do
- result = File.open(@name, "r:binary:utf-8") { |f| f.read }.chomp
- result.encoding.should == Encoding::BINARY
- xE2 = [226].pack('C*')
- result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
- end
end
describe "IO#read in text mode" do
diff --git a/spec/ruby/core/io/readchar_spec.rb b/spec/ruby/core/io/readchar_spec.rb
index b5f762a846..a66773851a 100644
--- a/spec/ruby/core/io/readchar_spec.rb
+++ b/spec/ruby/core/io/readchar_spec.rb
@@ -1,6 +1,16 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+describe :io_readchar_internal_encoding, shared: true do
+ it "returns a transcoded String" do
+ @io.readchar.should == "あ"
+ end
+
+ it "sets the String encoding to the internal encoding" do
+ @io.readchar.encoding.should equal(Encoding::UTF_8)
+ end
+end
+
describe "IO#readchar" do
before :each do
@io = IOSpecs.io_fixture "lines.txt"
@@ -29,6 +39,62 @@ describe "IO#readchar" do
end
end
+describe "IO#readchar with internal encoding" do
+ after :each do
+ @io.close if @io
+ end
+
+ describe "not specified" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp"
+ end
+
+ it "does not transcode the String" do
+ @io.readchar.should == ("あ").encode(Encoding::EUC_JP)
+ end
+
+ it "sets the String encoding to the external encoding" do
+ @io.readchar.encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "specified by open mode" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by mode: option" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", mode: "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by internal_encoding: option" do
+ before :each do
+ options = { mode: "r",
+ internal_encoding: "utf-8",
+ external_encoding: "euc-jp" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by encoding: option" do
+ before :each do
+ options = { mode: "r", encoding: "euc-jp:utf-8" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+end
+
describe "IO#readchar" do
before :each do
@io = IOSpecs.io_fixture "empty.txt"
diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb
index ca30f31e39..cf9f0dfc11 100644
--- a/spec/ruby/core/io/readline_spec.rb
+++ b/spec/ruby/core/io/readline_spec.rb
@@ -51,6 +51,10 @@ describe "IO#readline" do
it "returns an empty string when passed 0 as a limit" do
@io.readline(0).should == ""
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readline(2**128) }.should raise_error(RangeError)
+ end
end
describe "when passed separator and limit" do
diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb
index 15af6debbe..496003002d 100644
--- a/spec/ruby/core/io/readlines_spec.rb
+++ b/spec/ruby/core/io/readlines_spec.rb
@@ -106,6 +106,10 @@ describe "IO#readlines" do
it "raises ArgumentError when passed 0 as a limit" do
-> { @io.readlines(0) }.should raise_error(ArgumentError)
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readlines(2**128) }.should raise_error(RangeError)
+ end
end
describe "when passed chomp" do
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
index 2b33a0d5b1..2901b429c2 100644
--- a/spec/ruby/core/io/readpartial_spec.rb
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -59,7 +59,7 @@ describe "IO#readpartial" do
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing"
+ buffer = "existing content"
@wr.write("hello world")
@wr.close
@rd.readpartial(11, buffer)
@@ -93,4 +93,19 @@ describe "IO#readpartial" do
@rd.readpartial(0).should == ""
end
+ ruby_bug "#18421", ""..."3.0.4" do
+ it "clears and returns the given buffer if the length argument is 0" do
+ buffer = "existing content"
+ @rd.readpartial(0, buffer).should == buffer
+ buffer.should == ""
+ end
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @wr.write("abc")
+ @wr.close
+ @rd.readpartial(10, buffer)
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
end
diff --git a/spec/ruby/core/io/rewind_spec.rb b/spec/ruby/core/io/rewind_spec.rb
index 649041afaf..5579cbd988 100644
--- a/spec/ruby/core/io/rewind_spec.rb
+++ b/spec/ruby/core/io/rewind_spec.rb
@@ -18,6 +18,17 @@ describe "IO#rewind" do
@io.readline.should == "Voici la ligne une.\n"
end
+ it "positions the instance to the beginning of output for write-only IO" do
+ name = tmp("io_rewind_spec")
+ io = File.open(name, "w")
+ io.write("Voici la ligne une.\n")
+ io.rewind
+ io.pos.should == 0
+ ensure
+ io.close
+ rm_r name
+ end
+
it "positions the instance to the beginning of input and clears EOF" do
value = @io.read
@io.rewind
@@ -32,6 +43,10 @@ describe "IO#rewind" do
@io.lineno.should == 0
end
+ it "returns 0" do
+ @io.rewind.should == 0
+ end
+
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.rewind }.should raise_error(IOError)
end
diff --git a/spec/ruby/core/io/set_encoding_by_bom_spec.rb b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
index b52d3a943a..92433d6640 100644
--- a/spec/ruby/core/io/set_encoding_by_bom_spec.rb
+++ b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
@@ -12,45 +12,232 @@ describe "IO#set_encoding_by_bom" do
rm_r @name
end
+ it "returns nil if not readable" do
+ not_readable_io = new_io(@name, 'wb')
+
+ not_readable_io.set_encoding_by_bom.should be_nil
+ not_readable_io.external_encoding.should == Encoding::ASCII_8BIT
+ ensure
+ not_readable_io.close
+ end
+
it "returns the result encoding if found BOM UTF-8 sequence" do
+ File.binwrite(@name, "\u{FEFF}")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_8
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\u{FEFF}abc")
@io.set_encoding_by_bom.should == Encoding::UTF_8
@io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_16LE sequence" do
+ File.binwrite(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFF\xFEabc")
@io.set_encoding_by_bom.should == Encoding::UTF_16LE
@io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_16BE sequence" do
+ File.binwrite(@name, "\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ @io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFE\xFFabc")
@io.set_encoding_by_bom.should == Encoding::UTF_16BE
@io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_32LE sequence" do
+ File.binwrite(@name, "\xFF\xFE\x00\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ @io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\xFF\xFE\x00\x00abc")
@io.set_encoding_by_bom.should == Encoding::UTF_32LE
@io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "abc".b
end
it "returns the result encoding if found BOM UTF_32BE sequence" do
+ File.binwrite(@name, "\x00\x00\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ @io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
File.binwrite(@name, "\x00\x00\xFE\xFFabc")
@io.set_encoding_by_bom.should == Encoding::UTF_32BE
@io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "abc".b
+ end
+
+ it "returns nil if io is empty" do
+ @io.set_encoding_by_bom.should be_nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns nil if UTF-8 BOM sequence is incomplete" do
+ File.write(@name, "\xEF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF".b
+ @io.rewind
+
+ File.write(@name, "\xEFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEFa".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBB")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBB".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBBa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBBa".b
+ end
+
+ it "returns nil if UTF-16BE BOM sequence is incomplete" do
+ File.write(@name, "\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFE".b
+ @io.rewind
+
+ File.write(@name, "\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFEa".b
+ end
+
+ it "returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFF".b
+ @io.rewind
+
+ File.write(@name, "\xFFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFFa".b
+ end
+
+ it "returns UTF-16LE if UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00a")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00a".b
+ end
+
+ it "returns nil if UTF-32BE BOM sequence is incomplete" do
+ File.write(@name, "\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFE".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFEa".b
end
it "returns nil if found BOM sequence not provided" do
File.write(@name, "abc")
@io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read(3).should == "abc".b
end
it 'returns exception if io not in binary mode' do
diff --git a/spec/ruby/core/io/set_encoding_spec.rb b/spec/ruby/core/io/set_encoding_spec.rb
index 5aec6a96c3..22d9017635 100644
--- a/spec/ruby/core/io/set_encoding_spec.rb
+++ b/spec/ruby/core/io/set_encoding_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe :io_set_encoding_write, shared: true do
- it "sets the encodings to nil" do
+ it "sets the encodings to nil when they were set previously" do
@io = new_io @name, "#{@object}:ibm437:ibm866"
@io.set_encoding nil, nil
@@ -9,6 +9,19 @@ describe :io_set_encoding_write, shared: true do
@io.internal_encoding.should be_nil
end
+ it "sets the encodings to nil when the IO is built with no explicit encoding" do
+ @io = new_io @name, @object
+
+ # Checking our assumptions first
+ @io.external_encoding.should be_nil
+ @io.internal_encoding.should be_nil
+
+ @io.set_encoding nil, nil
+
+ @io.external_encoding.should be_nil
+ @io.internal_encoding.should be_nil
+ end
+
it "prevents the encodings from changing when Encoding defaults are changed" do
@io = new_io @name, "#{@object}:utf-8:us-ascii"
@io.set_encoding nil, nil
@@ -38,6 +51,7 @@ describe "IO#set_encoding when passed nil, nil" do
@external = Encoding.default_external
@internal = Encoding.default_internal
+ # The defaults
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil
@@ -113,6 +127,22 @@ describe "IO#set_encoding when passed nil, nil" do
describe "with 'a+' mode" do
it_behaves_like :io_set_encoding_write, nil, "a+"
end
+
+ describe "with standard IOs" do
+ it "correctly resets them" do
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+
+ begin
+ STDOUT.set_encoding(Encoding::US_ASCII, Encoding::ISO_8859_1)
+ ensure
+ STDOUT.set_encoding(nil, nil)
+ end
+
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+ end
+ end
end
describe "IO#set_encoding" do
@@ -188,4 +218,21 @@ describe "IO#set_encoding" do
@io.external_encoding.should == Encoding::UTF_8
@io.internal_encoding.should == Encoding::UTF_16BE
end
+
+ it "saves encoding options passed as a hash in the last argument" do
+ File.write(@name, "\xff")
+ io = File.open(@name)
+ io.set_encoding(Encoding::EUC_JP, Encoding::SHIFT_JIS, invalid: :replace, replace: ".")
+ io.read.should == "."
+ ensure
+ io.close
+ end
+
+ it "raises ArgumentError when no arguments are given" do
+ -> { @io.set_encoding() }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when too many arguments are given" do
+ -> { @io.set_encoding(1, 2, 3) }.should raise_error(ArgumentError)
+ end
end
diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb
index 607e7de03e..02bbe19c1a 100644
--- a/spec/ruby/core/io/shared/each.rb
+++ b/spec/ruby/core/io/shared/each.rb
@@ -77,6 +77,10 @@ describe :io_each, shared: true do
-> { @io.send(@method, 0){} }.should raise_error(ArgumentError)
end
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.send(@method, 2**128){} }.should raise_error(RangeError)
+ end
end
describe "when passed a String containing one space as a separator" do
@@ -113,6 +117,13 @@ describe :io_each, shared: true do
@io.send(@method, "") { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
describe "with both separator and limit" do
@@ -152,6 +163,13 @@ describe :io_each, shared: true do
@io.send(@method, "", 1024) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "", 1024) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
end
end
@@ -220,6 +238,14 @@ describe :io_each, shared: true do
]
end
end
+
+ describe "when passed too many arguments" do
+ it "raises ArgumentError" do
+ -> {
+ @io.send(@method, "", 1, "excess argument", chomp: true) {}
+ }.should raise_error(ArgumentError)
+ end
+ end
end
describe :io_each_default_separator, shared: true do
diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb
index f2a0970a40..7677aada6e 100644
--- a/spec/ruby/core/io/shared/new.rb
+++ b/spec/ruby/core/io/shared/new.rb
@@ -1,5 +1,7 @@
require_relative '../fixtures/classes'
+# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+
# This group of specs may ONLY contain specs that do successfully create
# an IO instance from the file descriptor returned by #new_fd helper.
describe :io_new, shared: true do
diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb
index d83a6c6692..3fdd3eb2b3 100644
--- a/spec/ruby/core/io/shared/pos.rb
+++ b/spec/ruby/core/io/shared/pos.rb
@@ -60,7 +60,13 @@ describe :io_set_pos, shared: true do
end
end
- it "does not accept Integers that don't fit in a C long" do
+ it "raises TypeError when cannot convert implicitly argument to Integer" do
+ File.open @fname do |io|
+ -> { io.send @method, Object.new }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+ end
+
+ it "does not accept Integers that don't fit in a C off_t" do
File.open @fname do |io|
-> { io.send @method, 2**128 }.should raise_error(RangeError)
end
diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb
index 479452b71c..7681e1b5c1 100644
--- a/spec/ruby/core/io/shared/readlines.rb
+++ b/spec/ruby/core/io/shared/readlines.rb
@@ -79,6 +79,10 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines
end
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { IO.send(@method, @name, 2**128, &@object) }.should raise_error(RangeError)
+ end
+
ruby_bug "#18767", ""..."3.3" do
describe "when passed limit" do
it "raises ArgumentError when passed 0 as a limit" do
diff --git a/spec/ruby/core/io/shared/write.rb b/spec/ruby/core/io/shared/write.rb
index 270b84ac39..9503f94212 100644
--- a/spec/ruby/core/io/shared/write.rb
+++ b/spec/ruby/core/io/shared/write.rb
@@ -69,16 +69,6 @@ describe :io_write, shared: true do
-> { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
end
- it "does not modify the passed argument" do
- File.open(@filename, "w") do |f|
- f.set_encoding(Encoding::IBM437)
- # A character whose codepoint differs between UTF-8 and IBM437
- f.write "ƒ".freeze
- end
-
- File.binread(@filename).bytes.should == [159]
- end
-
describe "on a pipe" do
before :each do
@r, @w = IO.pipe
diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb
index 8201ad47ca..e7f63cefec 100644
--- a/spec/ruby/core/io/sysread_spec.rb
+++ b/spec/ruby/core/io/sysread_spec.rb
@@ -6,7 +6,7 @@ describe "IO#sysread on a file" do
@file_name = tmp("IO_sysread_file") + $$.to_s
File.open(@file_name, "w") do |f|
# write some stuff
- f.write("012345678901234567890123456789")
+ f.write("012345678901234567890123456789\nabcdef")
end
@file = File.open(@file_name, "r+")
end
@@ -84,6 +84,29 @@ describe "IO#sysread on a file" do
it "raises IOError on closed stream" do
-> { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError)
end
+
+ it "immediately returns an empty string if the length argument is 0" do
+ @file.sysread(0).should == ""
+ end
+
+ it "immediately returns the given buffer if the length argument is 0" do
+ buffer = "existing content"
+ @file.sysread(0, buffer).should == buffer
+ buffer.should == "existing content"
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing content"
+ @file.sysread(11, buffer)
+ buffer.should == "01234567890"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = "existing content"
+ @file.seek(0, :END)
+ -> { @file.sysread(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
end
describe "IO#sysread" do
@@ -100,4 +123,10 @@ describe "IO#sysread" do
@write.syswrite "ab"
@read.sysread(3).should == "ab"
end
+
+ guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do # https://bugs.ruby-lang.org/issues/18880
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.sysread(-1) }.should raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/core/io/sysseek_spec.rb b/spec/ruby/core/io/sysseek_spec.rb
index e631939bce..002f2a14eb 100644
--- a/spec/ruby/core/io/sysseek_spec.rb
+++ b/spec/ruby/core/io/sysseek_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
require_relative 'shared/pos'
describe "IO#sysseek" do
- it_behaves_like :io_set_pos, :seek
+ it_behaves_like :io_set_pos, :sysseek
end
describe "IO#sysseek" do
diff --git a/spec/ruby/core/io/syswrite_spec.rb b/spec/ruby/core/io/syswrite_spec.rb
index a28cc62174..eeb8d302a7 100644
--- a/spec/ruby/core/io/syswrite_spec.rb
+++ b/spec/ruby/core/io/syswrite_spec.rb
@@ -29,6 +29,16 @@ describe "IO#syswrite on a file" do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.syswrite("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "warns if called immediately after a buffered IO#write" do
@file.write("abcde")
-> { @file.syswrite("fghij") }.should complain(/syswrite/)
diff --git a/spec/ruby/core/io/write_nonblock_spec.rb b/spec/ruby/core/io/write_nonblock_spec.rb
index a8b9ce0a36..5532556d8a 100644
--- a/spec/ruby/core/io/write_nonblock_spec.rb
+++ b/spec/ruby/core/io/write_nonblock_spec.rb
@@ -31,6 +31,16 @@ platform_is_not :windows do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write_nonblock("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "checks if the file is writable if writing zero bytes" do
-> {
@readonly_file.write_nonblock("")
diff --git a/spec/ruby/core/io/write_spec.rb b/spec/ruby/core/io/write_spec.rb
index f29fdf3a01..bcc0582d9e 100644
--- a/spec/ruby/core/io/write_spec.rb
+++ b/spec/ruby/core/io/write_spec.rb
@@ -44,6 +44,16 @@ describe "IO#write on a file" do
@file.write("hellø").should == 6
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [159]
+ end
+
it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8
diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb
index 4f043526b8..cc8177fa02 100644
--- a/spec/ruby/core/kernel/Complex_spec.rb
+++ b/spec/ruby/core/kernel/Complex_spec.rb
@@ -1,4 +1,6 @@
require_relative '../../spec_helper'
+require_relative '../../shared/kernel/complex'
+require_relative 'fixtures/Complex'
describe "Kernel.Complex()" do
describe "when passed [Complex, Complex]" do
@@ -58,7 +60,92 @@ describe "Kernel.Complex()" do
end
end
- describe "when passed a String" do
+ describe "when passed [String]" do
+ it_behaves_like :kernel_complex, :Complex_method, KernelSpecs
+
+ context "invalid argument" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"))
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "raises ArgumentError for unrecognised Strings" do
+ -> {
+ Complex("ruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "ruby"')
+ end
+
+ it "raises ArgumentError for trailing garbage" do
+ -> {
+ Complex("79+4iruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "79+4iruby"')
+ end
+
+ it "does not understand Float::INFINITY" do
+ -> {
+ Complex("Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "Infinity"')
+
+ -> {
+ Complex("-Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "-Infinity"')
+ end
+
+ it "does not understand Float::NAN" do
+ -> {
+ Complex("NaN")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "NaN"')
+ end
+
+ it "does not understand a sequence of _" do
+ -> {
+ Complex("7__9+4__0i")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "7__9+4__0i"')
+ end
+
+ it "does not allow null-byte" do
+ -> {
+ Complex("1-2i\0")
+ }.should raise_error(ArgumentError, "string contains null byte")
+ end
+ end
+
+ context "invalid argument and exception: false passed" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"), exception: false)
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "returns nil for unrecognised Strings" do
+ Complex("ruby", exception: false).should == nil
+ end
+
+ it "returns nil when trailing garbage" do
+ Complex("79+4iruby", exception: false).should == nil
+ end
+
+ it "returns nil for Float::INFINITY" do
+ Complex("Infinity", exception: false).should == nil
+ Complex("-Infinity", exception: false).should == nil
+ end
+
+ it "returns nil for Float::NAN" do
+ Complex("NaN", exception: false).should == nil
+ end
+
+ it "returns nil when there is a sequence of _" do
+ Complex("7__9+4__0i", exception: false).should == nil
+ end
+
+ it "returns nil when String contains null-byte" do
+ Complex("1-2i\0", exception: false).should == nil
+ end
+ end
+ end
+
+ describe "when passes [String, String]" do
it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/core/kernel/fixtures/Complex.rb b/spec/ruby/core/kernel/fixtures/Complex.rb
new file mode 100644
index 0000000000..bf14d55ad5
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/Complex.rb
@@ -0,0 +1,5 @@
+module KernelSpecs
+ def self.Complex_method(string)
+ Complex(string)
+ end
+end
diff --git a/spec/ruby/core/kernel/p_spec.rb b/spec/ruby/core/kernel/p_spec.rb
index 1bdd1740ca..eae191aa54 100644
--- a/spec/ruby/core/kernel/p_spec.rb
+++ b/spec/ruby/core/kernel/p_spec.rb
@@ -76,10 +76,8 @@ describe "Kernel#p" do
-> { p(*[]) }.should output("")
end
-=begin Not sure how to spec this, but wanted to note the behavior here
- it "does not flush if receiver is not a TTY or a File" do
- end
-=end
+ # Not sure how to spec this, but wanted to note the behavior here
+ it "does not flush if receiver is not a TTY or a File"
end
describe "Kernel.p" do
diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb
index 120619abef..5c41c19bf6 100644
--- a/spec/ruby/core/kernel/shared/load.rb
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -88,12 +88,12 @@ describe :kernel_load, shared: true do
describe "when passed true for 'wrap'" do
it "loads from an existing path" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true).should be_true
end
it "sets the enclosing scope to an anonymous module" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
Object.const_defined?(:LoadSpecWrap).should be_false
@@ -103,14 +103,14 @@ describe :kernel_load, shared: true do
end
it "allows referencing outside namespaces" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
ScratchPad.recorded[0].should equal(String)
end
it "sets self as a copy of the top-level main" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
@@ -127,7 +127,7 @@ describe :kernel_load, shared: true do
main_ancestors = main.singleton_class.ancestors[1..-1]
main_ancestors.first.should == mod
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
@@ -154,6 +154,41 @@ describe :kernel_load, shared: true do
end
end
+ describe "when passed a module for 'wrap'" do
+ ruby_version_is "3.1" do
+ it "sets the enclosing scope to the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ Object.const_defined?(:LoadSpecWrap).should be_false
+ mod.const_defined?(:LoadSpecWrap).should be_true
+
+ wrap_module = ScratchPad.recorded[1]
+ wrap_module.should == mod
+ end
+
+ it "makes constants and instance methods in the source file reachable with the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1
+ obj = Object.new
+ obj.extend(mod)
+ obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method
+ end
+
+ it "makes instance methods in the source file private" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true
+ end
+ end
+ end
+
describe "(shell expansion)" do
before :each do
@env_home = ENV["HOME"]
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 666ca15e11..ae814aa317 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -237,6 +237,17 @@ describe :kernel_require, shared: true do
}.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded]
end
+
+ ruby_bug "#17340", ''...'3.3' do
+ it "loads a file concurrently" do
+ path = File.expand_path "concurrent_require_fixture.rb", CODE_LOADING_DIR
+ ScratchPad.record(@object)
+ -> {
+ @object.require(path)
+ }.should_not complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.join
+ end
+ end
end
describe "(non-extensioned path)" do
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
index 59f5ab0036..2db50bd686 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -293,13 +293,13 @@ describe :kernel_sprintf, shared: true do
it "raises ArgumentError if argument is a string of several characters" do
-> {
@method.call("%c", "abc")
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /%c requires a character/)
end
it "raises ArgumentError if argument is an empty string" do
-> {
@method.call("%c", "")
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /%c requires a character/)
end
end
@@ -313,9 +313,56 @@ describe :kernel_sprintf, shared: true do
end
end
- it "supports Unicode characters" do
- @method.call("%c", 1286).should == "Ԇ"
- @method.call("%c", "ش").should == "ش"
+ it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
+ -> {
+ @method.call("%c", [])
+ }.should raise_error(TypeError, /no implicit conversion of Array into Integer/)
+ end
+
+ it "raises TypeError if argument is nil" do
+ -> {
+ @method.call("%c", nil)
+ }.should raise_error(TypeError, /no implicit conversion from nil to integer/)
+ end
+
+ it "tries to convert argument to String with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "a"
+ end
+
+ @method.call("%c", obj).should == "a"
+ end
+
+ it "tries to convert argument to Integer with to_int" do
+ obj = BasicObject.new
+ def obj.to_int
+ 90
+ end
+
+ @method.call("%c", obj).should == "Z"
+ end
+
+ it "raises TypeError if converting to String with to_str returns non-String" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to String/)
+ end
+
+ it "raises TypeError if converting to Integer with to_int returns non-Integer" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to String/)
end
end
@@ -374,11 +421,11 @@ describe :kernel_sprintf, shared: true do
@method.call("%4.6s", "abcdefg").should == "abcdef"
end
- it "formats nli with width" do
+ it "formats nil with width" do
@method.call("%6s", nil).should == " "
end
- it "formats nli with precision" do
+ it "formats nil with precision" do
@method.call("%.6s", nil).should == ""
end
@@ -939,4 +986,8 @@ describe :kernel_sprintf, shared: true do
}
end
end
+
+ it "does not raise error when passed more arguments than needed" do
+ sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c"
+ end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
index 5ca66b9083..9cedb8b662 100644
--- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -1,3 +1,5 @@
+# Keep encoding-related specs in a separate shared example to be able to skip them in IO/File/StringIO specs.
+# It's difficult to check result's encoding in the test after writing to a file/io buffer.
describe :kernel_sprintf_encoding, shared: true do
it "can produce a string with valid encoding" do
string = @method.call("good day %{valid}", valid: "e")
@@ -25,7 +27,7 @@ describe :kernel_sprintf_encoding, shared: true do
result.encoding.should equal(Encoding::UTF_8)
end
- it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there ano not ASCII characters" do
+ it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there are not ASCII characters" do
string = "Ä %s".encode('windows-1252')
argument = "Ђ".encode('windows-1251')
@@ -33,4 +35,33 @@ describe :kernel_sprintf_encoding, shared: true do
@method.call(string, argument)
}.should raise_error(Encoding::CompatibilityError)
end
+
+ describe "%c" do
+ it "supports Unicode characters" do
+ result = @method.call("%c", 1286)
+ result.should == "Ԇ"
+ result.bytes.should == [212, 134]
+
+ result = @method.call("%c", "ش")
+ result.should == "ش"
+ result.bytes.should == [216, 180]
+ end
+
+ it "raises error when a codepoint isn't representable in an encoding of a format string" do
+ format = "%c".encode("ASCII")
+
+ -> {
+ @method.call(format, 1286)
+ }.should raise_error(RangeError, /out of char range/)
+ end
+
+ it "uses the encoding of the format string to interpret codepoints" do
+ format = "%c".force_encoding("euc-jp")
+ result = @method.call(format, 9415601)
+
+ result.encoding.should == Encoding::EUC_JP
+ result.should == "é".encode(Encoding::EUC_JP)
+ result.bytes.should == [143, 171, 177]
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb
index a8bcd916d1..c56fa08cc1 100644
--- a/spec/ruby/core/kernel/singleton_class_spec.rb
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -1,3 +1,5 @@
+require_relative '../../spec_helper'
+
describe "Kernel#singleton_class" do
it "returns class extended from an object" do
x = Object.new
diff --git a/spec/ruby/core/main/fixtures/using.rb b/spec/ruby/core/main/fixtures/using.rb
new file mode 100644
index 0000000000..30713ef309
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using.rb
@@ -0,0 +1 @@
+using Module.new
diff --git a/spec/ruby/core/main/fixtures/using_in_main.rb b/spec/ruby/core/main/fixtures/using_in_main.rb
new file mode 100644
index 0000000000..a4a71c89cc
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_main.rb
@@ -0,0 +1,5 @@
+MAIN = self
+
+module X
+ MAIN.send(:using, Module.new)
+end
diff --git a/spec/ruby/core/main/fixtures/using_in_method.rb b/spec/ruby/core/main/fixtures/using_in_method.rb
new file mode 100644
index 0000000000..d9ea2e9ef0
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_method.rb
@@ -0,0 +1,5 @@
+def foo
+ using Module.new
+end
+
+foo
diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb
index f9f709f7dc..8a23970c4b 100644
--- a/spec/ruby/core/main/using_spec.rb
+++ b/spec/ruby/core/main/using_spec.rb
@@ -129,4 +129,24 @@ describe "main.using" do
x.call_bar(cls2.new).should == 'bar'
end
+
+ it "raises error when called from method in wrapped script" do
+ -> do
+ load File.expand_path('../fixtures/using_in_method.rb', __FILE__), true
+ end.should raise_error(RuntimeError)
+ end
+
+ it "raises error when called on toplevel from module" do
+ -> do
+ load File.expand_path('../fixtures/using_in_main.rb', __FILE__), true
+ end.should raise_error(RuntimeError)
+ end
+
+ ruby_version_is "3.2" do
+ it "does not raise error when wrapped with module" do
+ -> do
+ load File.expand_path('../fixtures/using.rb', __FILE__), true
+ end.should_not raise_error
+ end
+ end
end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
index 079010e554..879ea287ce 100644
--- a/spec/ruby/core/marshal/dump_spec.rb
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -1,5 +1,6 @@
# -*- encoding: binary -*-
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
require_relative 'fixtures/marshal_data'
describe "Marshal.dump" do
@@ -106,7 +107,7 @@ describe "Marshal.dump" do
end
describe "with an object responding to #_dump" do
- it "dumps the object returned by #marshal_dump" do
+ it "dumps the object returned by #_dump" do
Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
end
@@ -122,6 +123,34 @@ describe "Marshal.dump" do
m.should_not_receive(:_dump)
Marshal.dump(m)
end
+
+ it "indexes instance variables of a String returned by #_dump at first and then indexes the object itself" do
+ class MarshalSpec::M1::A
+ def _dump(level)
+ s = "<dump>"
+ s.instance_variable_set(:@foo, "bar")
+ s
+ end
+ end
+
+ a = MarshalSpec::M1::A.new
+
+ # 0-based index of the object a = 2, that is encoded as \x07 and printed as "\a" character.
+ # Objects are serialized in the following order: Array, a, "bar".
+ # But they are indexed in different order: Array (index=0), "bar" (index=1), a (index=2)
+ # So the second occurenc of the object a is encoded as an index 2.
+ reference = "@\a"
+ Marshal.dump([a, a]).should == "\x04\b[\aIu:\x17MarshalSpec::M1::A\v<dump>\x06:\t@foo\"\bbar#{reference}"
+ end
+
+ describe "Core library classes with #_dump returning a String with instance variables" do
+ it "indexes instance variables and then a Time object itself" do
+ t = Time.utc(2022)
+ reference = "@\a"
+
+ Marshal.dump([t, t]).should == "\x04\b[\aIu:\tTime\r \x80\x1E\xC0\x00\x00\x00\x00\x06:\tzoneI\"\bUTC\x06:\x06EF#{reference}"
+ end
+ end
end
describe "with a Class" do
@@ -185,6 +214,20 @@ describe "Marshal.dump" do
[Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
].should be_computed_by(:dump)
end
+
+ it "increases the object links counter" do
+ obj = Object.new
+ object_1_link = "\x06" # representing of (0-based) index=1 (by adding 5 for small Integers)
+ object_2_link = "\x07" # representing of index=2
+
+ # objects: Array, Object, Object
+ Marshal.dump([obj, obj]).should == "\x04\b[\ao:\vObject\x00@#{object_1_link}"
+
+ # objects: Array, Bignum, Object, Object
+ Marshal.dump([2**64, obj, obj]).should == "\x04\b[\bl+\n\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**48, obj, obj]).should == "\x04\b[\bl+\t\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**32, obj, obj]).should == "\x04\b[\bl+\b\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ end
end
describe "with a String" do
diff --git a/spec/ruby/core/marshal/fixtures/classes.rb b/spec/ruby/core/marshal/fixtures/classes.rb
new file mode 100644
index 0000000000..7c81c64927
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module MarshalSpec
+ # empty modules
+ module M1 end
+end
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 98fae44296..479c7477ec 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -720,6 +720,17 @@ describe :marshal_load, shared: true do
new_obj_metaclass_ancestors[@num_self_class, 3].should ==
[Meths, UserRegexp, Regexp]
end
+
+ ruby_bug "#19439", ""..."3.3" do
+ it "restore the regexp instance variables" do
+ obj = Regexp.new("hello")
+ obj.instance_variable_set(:@regexp_ivar, [42])
+
+ new_obj = Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ new_obj.instance_variables.should == [:@regexp_ivar]
+ new_obj.instance_variable_get(:@regexp_ivar).should == [42]
+ end
+ end
end
describe "for a Float" do
diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb
index 8965f902a0..7c0f089bb4 100644
--- a/spec/ruby/core/matchdata/element_reference_spec.rb
+++ b/spec/ruby/core/matchdata/element_reference_spec.rb
@@ -26,6 +26,21 @@ describe "MatchData#[]" do
it "supports ranges [start..end]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..10].should == %w|113 8|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[-30..2].should == nil
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..1].should == []
+ end
+
+ it "supports endless ranges [start..]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..].should == %w|113 8|
+ end
+
+ it "supports beginningless ranges [..end]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[..1].should == %w|HX1138 H|
+ end
+
+ it "supports beginningless endless ranges [nil..nil]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[nil..nil].should == %w|HX1138 H X 113 8|
end
ruby_version_is "3.0" do
diff --git a/spec/ruby/core/matchdata/values_at_spec.rb b/spec/ruby/core/matchdata/values_at_spec.rb
index 8f7fdf557c..4fd0bfc42a 100644
--- a/spec/ruby/core/matchdata/values_at_spec.rb
+++ b/spec/ruby/core/matchdata/values_at_spec.rb
@@ -1,21 +1,76 @@
require_relative '../../spec_helper'
-describe "MatchData#values_at" do
- it "returns an array of the matching value" do
- /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"]
+describe "Struct#values_at" do
+ # Should be synchronized with core/array/values_at_spec.rb and core/struct/values_at_spec.rb
+ #
+ # /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").to_a # => ["HX1138", "H", "X", "113", "8"]
+
+ context "when passed a list of Integers" do
+ it "returns an array containing each value given by one of integers" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"]
+ end
+
+ it "returns nil value for any integer that is out of range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(5).should == [nil]
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6).should == [nil]
+ end
end
- describe "when passed a Range" do
- it "returns an array of the matching value" do
- /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..4, 0..1).should == ["X", "113", "8", "HX1138", "H"]
+ context "when passed an integer Range" do
+ it "returns an array containing each value given by the elements of the range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"]
+ end
+
+ it "fills with nil values for range elements larger than the captured values number" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..5).should == ["HX1138", "H", "X", "113", "8", nil]
+ end
+
+ it "raises RangeError if any element of the range is negative and out of range" do
+ -> { /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6..3) }.should raise_error(RangeError, "-6..3 out of range")
+ end
+
+ it "supports endless Range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..).should == ["HX1138", "H", "X", "113", "8"]
+ end
+
+ it "supports beginningless Range" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"]
+ end
+
+ it "returns an empty Array when Range is empty" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..0).should == []
+ end
+ end
+
+ context "when passed names" do
+ it 'slices captures with the given names' do
+ /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0']
+ end
+
+ it 'slices captures with the given String names' do
+ /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at('c', 'a').should == ['2', '0']
end
end
- it 'slices captures with the given names' do
- /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0']
+ it "supports multiple integer Ranges" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 2..3).should == ["H", "X", "X", "113"]
end
- it 'takes names and indices' do
+ it "supports mixing integer Ranges and Integers" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 4).should == ["H", "X", "8"]
+ end
+
+ it 'supports mixing of names and indices' do
/\A(?<a>.)(?<b>.)\z/.match('01').values_at(0, 1, 2, :a, :b).should == ['01', '0', '1', '0', '1']
end
+
+ it "returns a new empty Array if no arguments given" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at().should == []
+ end
+
+ it "fails when passed arguments of unsupported types" do
+ -> {
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
end
diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb
index be96f65e25..464a519aea 100644
--- a/spec/ruby/core/method/fixtures/classes.rb
+++ b/spec/ruby/core/method/fixtures/classes.rb
@@ -84,6 +84,12 @@ module MethodSpecs
def two_req_one_opt_with_splat_and_block(a, b, c=nil, *d, &blk); end
def one_req_two_opt_with_splat_and_block(a, b=nil, c=nil, *d, &blk); end
+ def my_public_method; end
+ def my_protected_method; end
+ def my_private_method; end
+ protected :my_protected_method
+ private :my_private_method
+
define_method(:zero_defined_method, Proc.new {||})
define_method(:zero_with_splat_defined_method, Proc.new {|*x|})
define_method(:one_req_defined_method, Proc.new {|x|})
@@ -213,4 +219,28 @@ module MethodSpecs
n * m
end
end
+
+ module InheritedMethods
+ module A
+ private
+ def derp(message)
+ 'A'
+ end
+ end
+
+ module B
+ private
+ def derp
+ 'B' + super('superclass')
+ end
+ end
+
+ class C
+ include A
+ include B
+
+ public :derp
+ alias_method :meow, :derp
+ end
+ end
end
diff --git a/spec/ruby/core/method/owner_spec.rb b/spec/ruby/core/method/owner_spec.rb
index ca5dff7295..05422f1697 100644
--- a/spec/ruby/core/method/owner_spec.rb
+++ b/spec/ruby/core/method/owner_spec.rb
@@ -23,4 +23,10 @@ describe "Method#owner" do
@m.method(:handled_via_method_missing).owner.should == MethodSpecs::Methods
end
end
+
+ ruby_version_is "3.2" do
+ it "returns the class on which public was called for a private method in ancestor" do
+ MethodSpecs::InheritedMethods::C.new.method(:derp).owner.should == MethodSpecs::InheritedMethods::C
+ end
+ end
end
diff --git a/spec/ruby/core/method/private_spec.rb b/spec/ruby/core/method/private_spec.rb
new file mode 100644
index 0000000000..230a4e9e81
--- /dev/null
+++ b/spec/ruby/core/method/private_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#private?" do
+ it "returns false when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).private?.should == false
+ end
+
+ it "returns false when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).private?.should == false
+ end
+
+ it "returns true when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).private?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/method/protected_spec.rb b/spec/ruby/core/method/protected_spec.rb
new file mode 100644
index 0000000000..6ee85f7738
--- /dev/null
+++ b/spec/ruby/core/method/protected_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#protected?" do
+ it "returns false when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).protected?.should == false
+ end
+
+ it "returns true when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).protected?.should == true
+ end
+
+ it "returns false when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).protected?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/public_spec.rb b/spec/ruby/core/method/public_spec.rb
new file mode 100644
index 0000000000..3988468551
--- /dev/null
+++ b/spec/ruby/core/method/public_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1"..."3.2" do
+ describe "Method#public?" do
+ it "returns true when the method is public" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_public_method).public?.should == true
+ end
+
+ it "returns false when the method is protected" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_protected_method).public?.should == false
+ end
+
+ it "returns false when the method is private" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:my_private_method).public?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/super_method_spec.rb b/spec/ruby/core/method/super_method_spec.rb
index e5d8b87a06..f9a18f3878 100644
--- a/spec/ruby/core/method/super_method_spec.rb
+++ b/spec/ruby/core/method/super_method_spec.rb
@@ -42,4 +42,25 @@ describe "Method#super_method" do
method.super_method.should == nil
end
+
+ # https://github.com/jruby/jruby/issues/7240
+ context "after changing an inherited methods visibility" do
+ it "calls the proper super method" do
+ MethodSpecs::InheritedMethods::C.new.derp.should == 'BA'
+ end
+
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:derp)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+
+ ruby_version_is "2.7.3" do
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/method/unbind_spec.rb b/spec/ruby/core/method/unbind_spec.rb
index cdd3a808f2..bdedd513ce 100644
--- a/spec/ruby/core/method/unbind_spec.rb
+++ b/spec/ruby/core/method/unbind_spec.rb
@@ -27,8 +27,16 @@ describe "Method#unbind" do
@string.should =~ /MethodSpecs::MyMod/
end
- it "returns a String containing the Module the method is referenced from" do
- @string.should =~ /MethodSpecs::MySub/
+ ruby_version_is ""..."3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
end
end
diff --git a/spec/ruby/core/module/const_defined_spec.rb b/spec/ruby/core/module/const_defined_spec.rb
index 75730395e8..0c15629c08 100644
--- a/spec/ruby/core/module/const_defined_spec.rb
+++ b/spec/ruby/core/module/const_defined_spec.rb
@@ -17,11 +17,16 @@ describe "Module#const_defined?" do
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should be_true
end
- it "returns true if the constant is defined in a mixed-in module of the receiver" do
+ it "returns true if the constant is defined in a mixed-in module of the receiver's parent" do
# CS_CONST10 is defined in a module included by ChildA
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST10).should be_true
end
+ it "returns true if the constant is defined in a mixed-in module (with prepends) of the receiver" do
+ # CS_CONST11 is defined in the module included by ContainerPrepend
+ ConstantSpecs::ContainerPrepend.const_defined?(:CS_CONST11).should be_true
+ end
+
it "returns true if the constant is defined in Object and the receiver is a module" do
# CS_CONST1 is defined in Object
ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should be_true
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
index c65b30de24..ce94436bfd 100644
--- a/spec/ruby/core/module/define_method_spec.rb
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -219,18 +219,55 @@ describe "Module#define_method" do
o.block_test2.should == o
end
+ it "raises TypeError if name cannot converted to String" do
+ -> {
+ Class.new { define_method(1001, -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+
+ -> {
+ Class.new { define_method([], -> {}) }
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+ end
+
+ it "converts non-String name to String with #to_str" do
+ obj = Object.new
+ def obj.to_str() "foo" end
+
+ new_class = Class.new { define_method(obj, -> { :called }) }
+ new_class.new.foo.should == :called
+ end
+
+ it "raises TypeError when #to_str called on non-String name returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> {
+ Class.new { define_method(obj, -> {}) }
+ }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+
it "raises a TypeError when the given method is no Method/Proc" do
-> {
Class.new { define_method(:test, "self") }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)")
-> {
Class.new { define_method(:test, 1234) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)")
-> {
Class.new { define_method(:test, nil) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type NilClass (expected Proc/Method/UnboundMethod)")
+ end
+
+ it "uses provided Method/Proc even if block is specified" do
+ new_class = Class.new do
+ define_method(:test, -> { :method_is_called }) do
+ :block_is_called
+ end
+ end
+
+ new_class.new.test.should == :method_is_called
end
it "raises an ArgumentError when no block is given" do
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
index a64f672b2f..bc6b940a6c 100644
--- a/spec/ruby/core/module/fixtures/classes.rb
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -42,6 +42,14 @@ module ModuleSpecs
class LookupChild < Lookup
end
+ module ModuleWithPrepend
+ prepend LookupMod
+ end
+
+ class WithPrependedModule
+ include ModuleWithPrepend
+ end
+
class Parent
# For private_class_method spec
def self.private_method; end
@@ -380,6 +388,7 @@ module ModuleSpecs
# empty modules
module M1; end
module M2; end
+ module M3; end
module Autoload
def self.use_ex1
diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb
index c47e052d22..c073bc31ca 100644
--- a/spec/ruby/core/module/include_spec.rb
+++ b/spec/ruby/core/module/include_spec.rb
@@ -104,9 +104,9 @@ describe "Module#include" do
class A; include M; end
class B < A; include M; end
- all = [A,B,M]
+ all = [A, B, M]
- (B.ancestors & all).should == [B, A, M]
+ (B.ancestors.filter { |a| all.include?(a) }).should == [B, A, M]
end
end
diff --git a/spec/ruby/core/module/included_modules_spec.rb b/spec/ruby/core/module/included_modules_spec.rb
index 40e20953f4..ce94ed1285 100644
--- a/spec/ruby/core/module/included_modules_spec.rb
+++ b/spec/ruby/core/module/included_modules_spec.rb
@@ -4,9 +4,11 @@ require_relative 'fixtures/classes'
describe "Module#included_modules" do
it "returns a list of modules included in self" do
ModuleSpecs.included_modules.should == []
+
ModuleSpecs::Child.included_modules.should include(ModuleSpecs::Super, ModuleSpecs::Basic, Kernel)
ModuleSpecs::Parent.included_modules.should include(Kernel)
ModuleSpecs::Basic.included_modules.should == []
ModuleSpecs::Super.included_modules.should include(ModuleSpecs::Basic)
+ ModuleSpecs::WithPrependedModule.included_modules.should include(ModuleSpecs::ModuleWithPrepend)
end
end
diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb
index b4d6a0d8c8..8d006e647e 100644
--- a/spec/ruby/core/module/instance_method_spec.rb
+++ b/spec/ruby/core/module/instance_method_spec.rb
@@ -45,20 +45,46 @@ describe "Module#instance_method" do
@parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
@child_um.inspect.should =~ /\bfoo\b/
@child_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
- @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
@mod_um.inspect.should =~ /\bbar\b/
@mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/
- @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
+ ruby_version_is ""..."3.2" do
+ @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ end
end
- it "raises a TypeError if not passed a symbol" do
- -> { Object.instance_method([]) }.should raise_error(TypeError)
- -> { Object.instance_method(0) }.should raise_error(TypeError)
+ it "raises a TypeError if the given name is not a String/Symbol" do
+ -> { Object.instance_method([]) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(0) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(nil) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(mock('x')) }.should raise_error(TypeError, /is not a symbol nor a string/)
end
- it "raises a TypeError if the given name is not a string/symbol" do
- -> { Object.instance_method(nil) }.should raise_error(TypeError)
- -> { Object.instance_method(mock('x')) }.should raise_error(TypeError)
+ it "accepts String name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method(:foo)
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "accepts Symbol name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method("foo")
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "converts non-String name by calling #to_str method" do
+ obj = Object.new
+ def obj.to_str() "foo" end
+
+ method = ModuleSpecs::InstanceMeth.instance_method(obj)
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "raises TypeError when passed non-String name and #to_str returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_error(TypeError, /can't convert Object to String/)
end
it "raises a NameError if the method has been undefined" do
diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb
index d636e023ed..976b09b105 100644
--- a/spec/ruby/core/module/prepend_spec.rb
+++ b/spec/ruby/core/module/prepend_spec.rb
@@ -611,6 +611,18 @@ describe "Module#prepend" do
ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]]
end
+ it "prepends a module if it is included in a super class" do
+ module ModuleSpecs::M3
+ module M; end
+ class A; include M; end
+ class B < A; prepend M; end
+
+ all = [A, B, M]
+
+ (B.ancestors.filter { |a| all.include?(a) }).should == [M, B, A, M]
+ end
+ end
+
it "detects cyclic prepends" do
-> {
module ModuleSpecs::P
diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb
index 224078ae54..9ef7b5be44 100644
--- a/spec/ruby/core/module/shared/class_eval.rb
+++ b/spec/ruby/core/module/shared/class_eval.rb
@@ -55,40 +55,49 @@ describe :module_class_eval, shared: true do
it "converts a non-string filename to a string using to_str" do
(file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
ModuleSpecs.send(@method, "1+1", file)
+
+ (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
+ ModuleSpecs.send(@method, "1+1", file, 15)
end
it "raises a TypeError when the given filename can't be converted to string using to_str" do
(file = mock('123')).should_receive(:to_str).and_return(123)
- -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError, /can't convert MockObject to String/)
end
it "converts non string eval-string to string using to_str" do
(o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
ModuleSpecs.send(@method, o).should == 2
+
+ (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
+ ModuleSpecs.send(@method, o, "file.rb").should == 2
+
+ (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1")
+ ModuleSpecs.send(@method, o, "file.rb", 15).should == 2
end
it "raises a TypeError when the given eval-string can't be converted to string using to_str" do
o = mock('x')
- -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, "no implicit conversion of MockObject into String")
(o = mock('123')).should_receive(:to_str).and_return(123)
- -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, /can't convert MockObject to String/)
end
it "raises an ArgumentError when no arguments and no block are given" do
- -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError)
+ -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
it "raises an ArgumentError when more than 3 arguments are given" do
-> {
ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus")
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "raises an ArgumentError when a block and normal arguments are given" do
-> {
ModuleSpecs.send(@method, "1 + 1") { 1 + 1 }
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 1, expected 0)")
end
# This case was found because Rubinius was caching the compiled
diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb
index 281785b0a4..d9db027e0b 100644
--- a/spec/ruby/core/objectspace/define_finalizer_spec.rb
+++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb
@@ -169,4 +169,26 @@ describe "ObjectSpace.define_finalizer" do
ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"]
end
+
+ ruby_version_is "3.1" do
+ describe "when $VERBOSE is not nil" do
+ it "warns if an exception is raised in finalizer" do
+ code = <<-RUBY
+ ObjectSpace.define_finalizer(Object.new) { raise "finalizing" }
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should include("warning: Exception in finalizer", "finalizing")
+ end
+ end
+
+ describe "when $VERBOSE is nil" do
+ it "does not warn even if an exception is raised in finalizer" do
+ code = <<-RUBY
+ ObjectSpace.define_finalizer(Object.new) { raise "finalizing" }
+ RUBY
+
+ ruby_exe(code, args: "2>&1", options: "-W0").should == ""
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/process/_fork_spec.rb b/spec/ruby/core/process/_fork_spec.rb
new file mode 100644
index 0000000000..6f711ad2dd
--- /dev/null
+++ b/spec/ruby/core/process/_fork_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.1" do
+ describe "Process._fork" do
+ it "for #respond_to? returns the same as Process.respond_to?(:fork)" do
+ Process.respond_to?(:_fork).should == Process.respond_to?(:fork)
+ end
+
+ guard_not -> { Process.respond_to?(:fork) } do
+ it "raises a NotImplementedError when called" do
+ -> { Process._fork }.should raise_error(NotImplementedError)
+ end
+ end
+
+ guard -> { Process.respond_to?(:fork) } do
+ it "is called by Process#fork" do
+ Process.should_receive(:_fork).once.and_return(42)
+
+ pid = Process.fork {}
+ pid.should equal(42)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb
index b61f5ab64e..4130bb58a5 100644
--- a/spec/ruby/core/process/constants_spec.rb
+++ b/spec/ruby/core/process/constants_spec.rb
@@ -1,3 +1,4 @@
+require_relative '../../spec_helper'
describe "Process::Constants" do
platform_is :darwin, :netbsd, :freebsd do
diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb
index 70ffd1b320..20b0d743b9 100644
--- a/spec/ruby/core/process/daemon_spec.rb
+++ b/spec/ruby/core/process/daemon_spec.rb
@@ -2,6 +2,9 @@ require_relative '../../spec_helper'
require_relative 'fixtures/common'
platform_is_not :windows do
+ # macOS 15 is not working this examples
+ return if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion`
+
describe :process_daemon_keep_stdio_open_false, shared: true do
it "redirects stdout to /dev/null" do
@daemon.invoke("keep_stdio_open_false_stdout", @object).should == ""
diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb
index 1c27ed9c2c..91661afcea 100644
--- a/spec/ruby/core/process/detach_spec.rb
+++ b/spec/ruby/core/process/detach_spec.rb
@@ -42,5 +42,34 @@ describe "Process.detach" do
thr.pid.should == pid
end
+
+ it "tolerates not existing child process pid" do
+ # ensure there is no child process with this hardcoded pid
+ # `kill 0 pid` for existing process returns "1" and raises Errno::ESRCH if process doesn't exist
+ -> { Process.kill(0, 100500) }.should raise_error(Errno::ESRCH)
+
+ thr = Process.detach(100500)
+ thr.join
+
+ thr.should be_kind_of(Thread)
+ end
+
+ it "calls #to_int to implicitly convert non-Integer pid to Integer" do
+ pid = MockObject.new('mock-enumerable')
+ pid.should_receive(:to_int).and_return(100500)
+
+ Process.detach(pid).join
+ end
+
+ it "raises TypeError when pid argument does not have #to_int method" do
+ -> { Process.detach(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises TypeError when #to_int returns non-Integer value" do
+ pid = MockObject.new('mock-enumerable')
+ pid.should_receive(:to_int).and_return(:symbol)
+
+ -> { Process.detach(pid) }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Symbol)")
+ end
end
end
diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb
index 9aa8da8125..c8a58c4d04 100644
--- a/spec/ruby/core/process/spawn_spec.rb
+++ b/spec/ruby/core/process/spawn_spec.rb
@@ -477,6 +477,16 @@ describe "Process.spawn" do
# redirection
+ it 'redirects to the wrapped IO using wrapped_io.to_io if out: wrapped_io' do
+ File.open(@name, 'w') do |file|
+ -> do
+ wrapped_io = mock('wrapped IO')
+ wrapped_io.should_receive(:to_io).and_return(file)
+ Process.wait Process.spawn('echo Hello World', out: wrapped_io)
+ end.should output_to_fd("Hello World\n", file)
+ end
+ end
+
it "redirects STDOUT to the given file descriptor if out: Integer" do
File.open(@name, 'w') do |file|
-> do
@@ -567,6 +577,24 @@ describe "Process.spawn" do
end
end
+ platform_is_not :windows do
+ it "redirects non-default file descriptor to itself" do
+ File.open(@name, 'w') do |file|
+ -> do
+ Process.wait Process.spawn(
+ ruby_cmd("f = IO.new(#{file.fileno}, 'w'); f.print(:bang); f.flush"), file.fileno => file.fileno)
+ end.should output_to_fd("bang", file)
+ end
+ end
+ end
+
+ it "redirects default file descriptor to itself" do
+ -> do
+ Process.wait Process.spawn(
+ ruby_cmd("f = IO.new(#{STDOUT.fileno}, 'w'); f.print(:bang); f.flush"), STDOUT.fileno => STDOUT.fileno)
+ end.should output_to_fd("bang", STDOUT)
+ end
+
# :close_others
platform_is_not :windows do
diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb
index b47189a7e7..6142cd257c 100644
--- a/spec/ruby/core/process/times_spec.rb
+++ b/spec/ruby/core/process/times_spec.rb
@@ -5,12 +5,16 @@ describe "Process.times" do
Process.times.should be_kind_of(Process::Tms)
end
- it "returns current cpu times" do
- t = Process.times
- user = t.utime
+ # TODO: Intel C Compiler does not work this example
+ # http://rubyci.s3.amazonaws.com/icc-x64/ruby-master/log/20221013T030005Z.fail.html.gz
+ unless RbConfig::CONFIG['CC']&.include?("icx")
+ it "returns current cpu times" do
+ t = Process.times
+ user = t.utime
- 1 until Process.times.utime > user
- Process.times.utime.should > user
+ 1 until Process.times.utime > user
+ Process.times.utime.should > user
+ end
end
platform_is_not :windows do
diff --git a/spec/ruby/core/queue/initialize_spec.rb b/spec/ruby/core/queue/initialize_spec.rb
index 83c7e595fe..c45abcd29d 100644
--- a/spec/ruby/core/queue/initialize_spec.rb
+++ b/spec/ruby/core/queue/initialize_spec.rb
@@ -7,6 +7,10 @@ describe "Queue#initialize" do
q.should.empty?
end
+ it "is a private method" do
+ Queue.private_instance_methods.include?(:initialize).should == true
+ end
+
ruby_version_is '3.1' do
it "adds all elements of the passed Enumerable to self" do
q = Queue.new([1, 2, 3])
@@ -30,9 +34,16 @@ describe "Queue#initialize" do
q.should.empty?
end
- it "raises if the provided Enumerable does not respond to #to_a" do
+ it "raises TypeError if the provided Enumerable does not respond to #to_a" do
enumerable = MockObject.new('mock-enumerable')
-> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject into Array")
end
+
+ it "raises TypeError if #to_a does not return Array" do
+ enumerable = MockObject.new('mock-enumerable')
+ enumerable.should_receive(:to_a).and_return("string")
+
+ -> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_a gives String)")
+ end
end
end
diff --git a/spec/ruby/core/range/case_compare_spec.rb b/spec/ruby/core/range/case_compare_spec.rb
index 4a3faa3163..65878aaabe 100644
--- a/spec/ruby/core/range/case_compare_spec.rb
+++ b/spec/ruby/core/range/case_compare_spec.rb
@@ -10,4 +10,10 @@ describe "Range#===" do
it_behaves_like :range_cover_and_include, :===
it_behaves_like :range_cover, :===
+
+ ruby_bug "#19533", "3.2"..."3.3" do
+ it "returns true on any value if begin and end are both nil" do
+ (nil..nil).should === 1
+ end
+ end
end
diff --git a/spec/ruby/core/range/last_spec.rb b/spec/ruby/core/range/last_spec.rb
index d7ef776b42..6698686dd5 100644
--- a/spec/ruby/core/range/last_spec.rb
+++ b/spec/ruby/core/range/last_spec.rb
@@ -8,6 +8,12 @@ describe "Range#last" do
(1..5).last(3).should == [3, 4, 5]
end
+ ruby_bug '#18994', '2.7'...'3.2' do
+ it "returns the specified number if elements for single element inclusive range" do
+ (1..1).last(1).should == [1]
+ end
+ end
+
it "returns an empty array for an empty Range" do
(0...0).last(2).should == []
end
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 5462a1a5e1..9b625c9963 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -34,11 +34,28 @@ describe "Range#size" do
eval("([]...)").size.should == nil
end
- it 'returns Float::INFINITY for all beginless ranges' do
- (..1).size.should == Float::INFINITY
- (...0.5).size.should == Float::INFINITY
- (..nil).size.should == Float::INFINITY
- (...'o').size.should == Float::INFINITY
+ ruby_version_is ""..."3.2" do
+ it 'returns Float::INFINITY for all beginless ranges' do
+ (..1).size.should == Float::INFINITY
+ (...0.5).size.should == Float::INFINITY
+ (..nil).size.should == Float::INFINITY
+ (...'o').size.should == Float::INFINITY
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it 'returns Float::INFINITY for all beginless ranges if the start is numeric' do
+ (..1).size.should == Float::INFINITY
+ (...0.5).size.should == Float::INFINITY
+ end
+
+ it 'returns nil for all beginless ranges if the start is numeric' do
+ (...'o').size.should == nil
+ end
+
+ it 'returns nil if the start and the end is both nil' do
+ (nil..nil).size.should == nil
+ end
end
it "returns nil if first and last are not Numeric" do
diff --git a/spec/ruby/core/refinement/import_methods_spec.rb b/spec/ruby/core/refinement/import_methods_spec.rb
new file mode 100644
index 0000000000..1c526f5822
--- /dev/null
+++ b/spec/ruby/core/refinement/import_methods_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#import_methods" do
+ ruby_version_is "3.1" do
+ context "when methods are defined in Ruby code" do
+ it "imports methods" do
+ str_utils = Module.new do
+ def indent(level)
+ " " * level + self
+ end
+ end
+
+ Module.new do
+ refine String do
+ import_methods str_utils
+ "foo".indent(3).should == " foo"
+ end
+ end
+ end
+ end
+
+ context "when methods are not defined in Ruby code" do
+ it "raises ArgumentError" do
+ Module.new do
+ refine String do
+ -> {
+ import_methods Kernel
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/refinement/include_spec.rb b/spec/ruby/core/refinement/include_spec.rb
new file mode 100644
index 0000000000..25a53f0ec7
--- /dev/null
+++ b/spec/ruby/core/refinement/include_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#include" do
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about deprecation" do
+ Module.new do
+ refine String do
+ -> {
+ include Module.new
+ }.should complain(/warning: Refinement#include is deprecated and will be removed in Ruby 3.2/)
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises a TypeError" do
+ Module.new do
+ refine String do
+ -> {
+ include Module.new
+ }.should raise_error(TypeError, "Refinement#include has been removed")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/refinement/prepend_spec.rb b/spec/ruby/core/refinement/prepend_spec.rb
new file mode 100644
index 0000000000..27b70d392a
--- /dev/null
+++ b/spec/ruby/core/refinement/prepend_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Refinement#prepend" do
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about deprecation" do
+ Module.new do
+ refine String do
+ -> {
+ prepend Module.new
+ }.should complain(/warning: Refinement#prepend is deprecated and will be removed in Ruby 3.2/)
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises a TypeError" do
+ Module.new do
+ refine String do
+ -> {
+ prepend Module.new
+ }.should raise_error(TypeError, "Refinement#prepend has been removed")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/compile_spec.rb b/spec/ruby/core/regexp/compile_spec.rb
index 329cb4f753..c41399cfbb 100644
--- a/spec/ruby/core/regexp/compile_spec.rb
+++ b/spec/ruby/core/regexp/compile_spec.rb
@@ -13,3 +13,7 @@ end
describe "Regexp.compile given a Regexp" do
it_behaves_like :regexp_new_regexp, :compile
end
+
+describe "Regexp.new given a non-String/Regexp" do
+ it_behaves_like :regexp_new_non_string_or_regexp, :compile
+end
diff --git a/spec/ruby/core/regexp/initialize_spec.rb b/spec/ruby/core/regexp/initialize_spec.rb
index 772a233e82..a1583384af 100644
--- a/spec/ruby/core/regexp/initialize_spec.rb
+++ b/spec/ruby/core/regexp/initialize_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Regexp#initialize" do
it "is a private method" do
- Regexp.should have_private_method(:initialize)
+ Regexp.should have_private_instance_method(:initialize)
end
ruby_version_is ""..."3.0" do
diff --git a/spec/ruby/core/regexp/new_spec.rb b/spec/ruby/core/regexp/new_spec.rb
index ce662b7a4f..65f612df55 100644
--- a/spec/ruby/core/regexp/new_spec.rb
+++ b/spec/ruby/core/regexp/new_spec.rb
@@ -11,17 +11,9 @@ end
describe "Regexp.new given a Regexp" do
it_behaves_like :regexp_new_regexp, :new
- it_behaves_like :regexp_new_string_binary, :compile
+ it_behaves_like :regexp_new_string_binary, :new
end
-describe "Regexp.new given an Integer" do
- it "raises a TypeError" do
- -> { Regexp.new(1) }.should raise_error(TypeError)
- end
-end
-
-describe "Regexp.new given a Float" do
- it "raises a TypeError" do
- -> { Regexp.new(1.0) }.should raise_error(TypeError)
- end
+describe "Regexp.new given a non-String/Regexp" do
+ it_behaves_like :regexp_new_non_string_or_regexp, :new
end
diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb
index a6d9c48112..058a51b1aa 100644
--- a/spec/ruby/core/regexp/shared/new.rb
+++ b/spec/ruby/core/regexp/shared/new.rb
@@ -24,6 +24,32 @@ describe :regexp_new, shared: true do
end
end
+describe :regexp_new_non_string_or_regexp, shared: true do
+ it "calls #to_str method for non-String/Regexp argument" do
+ obj = Object.new
+ def obj.to_str() "a" end
+
+ Regexp.send(@method, obj).should == /a/
+ end
+
+ it "raises TypeError if there is no #to_str method for non-String/Regexp argument" do
+ obj = Object.new
+ -> { Regexp.send(@method, obj) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+
+ -> { Regexp.send(@method, 1) }.should raise_error(TypeError, "no implicit conversion of Integer into String")
+ -> { Regexp.send(@method, 1.0) }.should raise_error(TypeError, "no implicit conversion of Float into String")
+ -> { Regexp.send(@method, :symbol) }.should raise_error(TypeError, "no implicit conversion of Symbol into String")
+ -> { Regexp.send(@method, []) }.should raise_error(TypeError, "no implicit conversion of Array into String")
+ end
+
+ it "raises TypeError if #to_str returns non-String value" do
+ obj = Object.new
+ def obj.to_str() [] end
+
+ -> { Regexp.send(@method, obj) }.should raise_error(TypeError, /can't convert Object to String/)
+ end
+end
+
describe :regexp_new_string, shared: true do
it "uses the String argument as an unescaped literal to construct a Regexp object" do
Regexp.send(@method, "^hi{2,3}fo.o$").should == /^hi{2,3}fo.o$/
@@ -97,6 +123,16 @@ describe :regexp_new_string, shared: true do
(r.options & Regexp::EXTENDED).should_not == 0
end
+ it "does not try to convert the second argument to Integer with #to_int method call" do
+ ScratchPad.clear
+ obj = Object.new
+ def obj.to_int() ScratchPad.record(:called) end
+
+ Regexp.send(@method, "Hi", obj)
+
+ ScratchPad.recorded.should == nil
+ end
+
ruby_version_is ""..."3.2" do
it "treats any non-Integer, non-nil, non-false second argument as IGNORECASE" do
r = Regexp.send(@method, 'Hi', Object.new)
@@ -161,48 +197,50 @@ describe :regexp_new_string, shared: true do
end
end
- it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ ruby_version_is ""..."3.2" do
+ it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do
- -> {
- Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII
- }.should complain(/encoding option is ignored/)
- end
+ it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do
+ -> {
+ Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII
+ }.should complain(/encoding option is ignored/)
+ end
- it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do
- Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII
- Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII
- end
+ it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do
+ Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII
+ Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII
+ end
- it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do
- a = "(?:[\x8E\xA1-\xFE])"
- str = "\A(?:#{a}|x*)\z"
+ it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do
+ a = "(?:[\x8E\xA1-\xFE])"
+ str = "\A(?:#{a}|x*)\z"
- Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::BINARY
- Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::BINARY
+ Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::BINARY
+ end
end
describe "with escaped characters" do
@@ -562,8 +600,10 @@ describe :regexp_new_regexp, shared: true do
Regexp.send(@method, /Hi/n).encoding.should == Encoding::US_ASCII
end
- it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do
- Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::BINARY
+ ruby_version_is ''...'3.2' do
+ it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do
+ Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::BINARY
+ end
end
end
end
diff --git a/spec/ruby/core/regexp/timeout_spec.rb b/spec/ruby/core/regexp/timeout_spec.rb
new file mode 100644